diff --git a/.buildkite/ftr_platform_stateful_configs.yml b/.buildkite/ftr_platform_stateful_configs.yml index 6958ec4530ae8..244f04257ef92 100644 --- a/.buildkite/ftr_platform_stateful_configs.yml +++ b/.buildkite/ftr_platform_stateful_configs.yml @@ -344,7 +344,7 @@ enabled: - x-pack/test/task_manager_claimer_mget/config.ts - x-pack/test/ui_capabilities/security_and_spaces/config.ts - x-pack/test/ui_capabilities/spaces_only/config.ts - - x-pack/test/upgrade_assistant_integration/config.js + - x-pack/test/upgrade_assistant_integration/config.ts - x-pack/test/usage_collection/config.ts - x-pack/performance/journeys_e2e/aiops_log_rate_analysis.ts - x-pack/performance/journeys_e2e/ecommerce_dashboard.ts diff --git a/.buildkite/pipeline-resource-definitions/kibana-es-snapshots.yml b/.buildkite/pipeline-resource-definitions/kibana-es-snapshots.yml index 7146363bcc552..5a2521bb23026 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-es-snapshots.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-es-snapshots.yml @@ -53,6 +53,10 @@ spec: cronline: 0 22 * * * America/New_York message: Daily build branch: '8.x' + Daily build (8.16): + cronline: 0 22 * * * America/New_York + message: Daily build + branch: '8.16' Daily build (8.15): cronline: 0 22 * * * America/New_York message: Daily build diff --git a/.eslintrc.js b/.eslintrc.js index 006f39ce1026c..3c67594513c0e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -643,6 +643,7 @@ module.exports = { 'x-pack/test/*/*config.*ts', 'x-pack/test/saved_object_api_integration/*/apis/**/*', 'x-pack/test/ui_capabilities/*/tests/**/*', + 'x-pack/test/upgrade_assistant_integration/**/*', 'x-pack/test/performance/**/*.ts', '**/cypress.config.{js,ts}', 'x-pack/test_serverless/**/config*.ts', diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 56fe95cd65b39..707592b94a18b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -597,6 +597,7 @@ packages/kbn-management/settings/types @elastic/kibana-management packages/kbn-management/settings/utilities @elastic/kibana-management packages/kbn-management/storybook/config @elastic/kibana-management test/plugin_functional/plugins/management_test_plugin @elastic/kibana-management +packages/kbn-manifest @elastic/kibana-core packages/kbn-mapbox-gl @elastic/kibana-presentation x-pack/examples/third_party_maps_source_example @elastic/kibana-presentation src/plugins/maps_ems @elastic/kibana-presentation @@ -728,6 +729,7 @@ packages/kbn-resizable-layout @elastic/kibana-data-discovery examples/resizable_layout_examples @elastic/kibana-data-discovery x-pack/test/plugin_functional/plugins/resolver_test @elastic/security-solution packages/response-ops/feature_flag_service @elastic/response-ops +packages/response-ops/rule_params @elastic/response-ops examples/response_stream @elastic/ml-ui packages/kbn-rison @elastic/kibana-operations x-pack/packages/rollup @elastic/kibana-management @@ -929,9 +931,9 @@ packages/kbn-test-eui-helpers @elastic/kibana-visualizations x-pack/test/licensing_plugin/plugins/test_feature_usage @elastic/kibana-security packages/kbn-test-jest-helpers @elastic/kibana-operations @elastic/appex-qa packages/kbn-test-subj-selector @elastic/kibana-operations @elastic/appex-qa -x-pack/test_serverless -test -x-pack/test +x-pack/test_serverless +test +x-pack/test x-pack/performance @elastic/appex-qa x-pack/examples/testing_embedded_lens @elastic/kibana-visualizations x-pack/examples/third_party_lens_navigation_prompt @elastic/kibana-visualizations @@ -1087,6 +1089,8 @@ src/plugins/discover/public/context_awareness/profile_providers/security @elasti /x-pack/test_serverless/functional/test_suites/security/config.screenshots.ts @elastic/platform-docs # Visualizations +/x-pack/test/accessibility/apps/group3/graph.ts @elastic/kibana-visualizations +/x-pack/test/accessibility/apps/group2/lens.ts @elastic/kibana-visualizations /src/plugins/visualize/ @elastic/kibana-visualizations /x-pack/test/functional/apps/lens @elastic/kibana-visualizations /x-pack/test/api_integration/apis/lens/ @elastic/kibana-visualizations @@ -1114,6 +1118,7 @@ packages/kbn-monaco/src/esql @elastic/kibana-esql /docs/settings/reporting-settings.asciidoc @elastic/appex-sharedux /docs/setup/configuring-reporting.asciidoc @elastic/appex-sharedux /x-pack/test_serverless/**/test_suites/common/reporting/ @elastic/appex-sharedux +/x-pack/test/accessibility/apps/group3/reporting.ts @elastic/appex-sharedux ### Global Experience Tagging /x-pack/test/saved_object_tagging/ @elastic/appex-sharedux @@ -1200,6 +1205,8 @@ x-pack/test_serverless/**/test_suites/observability/ai_assistant @elastic/obs-ai /x-pack/test/functional/apps/infra/logs @elastic/obs-ux-logs-team # Observability UX management team +/x-pack/test/accessibility/apps/group1/uptime.ts @elastic/obs-ux-management-team +/x-pack/test/accessibility/apps/group3/observability.ts @elastic/obs-ux-management-team /x-pack/packages/observability/alert_details @elastic/obs-ux-management-team /x-pack/test/observability_functional @elastic/obs-ux-management-team /x-pack/plugins/observability_solution/infra/public/alerting @elastic/obs-ux-management-team @@ -1216,6 +1223,8 @@ x-pack/test_serverless/**/test_suites/observability/ai_assistant @elastic/obs-ai /x-pack/test/functional/apps/monitoring @elastic/stack-monitoring /x-pack/test/api_integration/apis/monitoring @elastic/stack-monitoring /x-pack/test/api_integration/apis/monitoring_collection @elastic/stack-monitoring +/x-pack/test/accessibility/apps/group1/kibana_overview.ts @elastic/stack-monitoring +/x-pack/test/accessibility/apps/group3/stack_monitoring.ts @elastic/stack-monitoring # Fleet /x-pack/test/fleet_api_integration @elastic/fleet @@ -1268,6 +1277,10 @@ x-pack/test_serverless/**/test_suites/observability/ai_assistant @elastic/obs-ai ### END Observability Plugins # Presentation +/x-pack/test/accessibility/apps/group3/maps.ts @elastic/kibana-presentation +/x-pack/test/accessibility/apps/group1/dashboard_panel_options.ts @elastic/kibana-presentation +/x-pack/test/accessibility/apps/group1/dashboard_links.ts @elastic/kibana-presentation +/x-pack/test/accessibility/apps/group1/dashboard_controls.ts @elastic/kibana-presentation /test/functional/apps/dashboard/ @elastic/kibana-presentation /test/functional/apps/dashboard_elements/ @elastic/kibana-presentation /test/functional/services/dashboard/ @elastic/kibana-presentation @@ -1341,6 +1354,9 @@ x-pack/test_serverless/**/test_suites/observability/ai_assistant @elastic/obs-ai /.eslintignore @elastic/kibana-operations # Appex QA +/x-pack/test/accessibility/services.ts @elastic/appex-qa +/x-pack/test/accessibility/page_objects.ts @elastic/appex-qa +/x-pack/test/accessibility/ftr_provider_context.d.ts @elastic/appex-qa /x-pack/test_serverless/tsconfig.json @elastic/appex-qa /x-pack/test_serverless/kibana.jsonc @elastic/appex-qa /x-pack/test_serverless/functional/test_suites/common/README.md @elastic/appex-qa @@ -1457,6 +1473,7 @@ x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts @elastic/kib #CC# /x-pack/plugins/security/ @elastic/kibana-security # Response Ops team +/x-pack/test/accessibility/apps/group3/rules_connectors.ts @elastic/response-ops /x-pack/test/functional/es_archives/cases/default @elastic/response-ops /x-pack/test_serverless/api_integration/test_suites/observability/config.ts @elastic/response-ops /x-pack/test_serverless/api_integration/test_suites/observability/index.ts @elastic/response-ops @@ -1529,10 +1546,26 @@ x-pack/test/api_integration/apis/management/index_management/inference_endpoints /x-pack/test/api_integration/apis/management/ @elastic/kibana-management /x-pack/test/functional/apps/rollup_job/ @elastic/kibana-management /x-pack/test/api_integration/apis/grok_debugger @elastic/kibana-management +/x-pack/test/accessibility/apps/group1/advanced_settings.ts @elastic/kibana-management +/x-pack/test/accessibility/apps/**/grok_debugger.ts @elastic/kibana-management +/x-pack/test/accessibility/apps/group1/helpers.ts @elastic/kibana-management +/x-pack/test/accessibility/apps/group1/home.ts @elastic/kibana-management +/x-pack/test/accessibility/apps/group1/index_lifecycle_management.ts @elastic/kibana-management +/x-pack/test/accessibility/apps/group1/ingest_node_pipelines.ts @elastic/kibana-management +/x-pack/test/accessibility/apps/group1/management.ts @elastic/kibana-management +/x-pack/test/accessibility/apps/group1/painless_lab.ts @elastic/kibana-management +/x-pack/test/accessibility/apps/group1/search_profiler.ts @elastic/kibana-management +/x-pack/test/accessibility/apps/group3/cross_cluster_replication.ts @elastic/kibana-management +/x-pack/test/accessibility/apps/group3/license_management.ts @elastic/kibana-management +/x-pack/test/accessibility/apps/group3/remote_clusters.ts @elastic/kibana-management +/x-pack/test/accessibility/apps/group3/rollup_jobs.ts @elastic/kibana-management +/x-pack/test/accessibility/apps/group3/upgrade_assistant.ts @elastic/kibana-management +/x-pack/test/accessibility/apps/group3/watcher.ts @elastic/kibana-management #CC# /x-pack/plugins/cross_cluster_replication/ @elastic/kibana-management # Security Solution +/x-pack/test/accessibility/apps/group3/security_solution.ts @elastic/security-solution /x-pack/test_serverless/functional/test_suites/security/config.ts @elastic/security-solution @elastic/appex-qa /x-pack/test_serverless/functional/test_suites/security/config.feature_flags.ts @elastic/security-solution /x-pack/test_serverless/api_integration/test_suites/observability/config.feature_flags.ts @elastic/security-solution @@ -1889,6 +1922,7 @@ x-pack/plugins/security_solution/server/lib/security_integrations @elastic/secur # Ent. Search design /x-pack/plugins/enterprise_search/**/*.scss @elastic/search-design +/x-pack/test/accessibility/apps/group3/enterprise_search.ts @elastic/search-kibana # Security design /x-pack/plugins/endpoint/**/*.scss @elastic/security-design @@ -1916,6 +1950,8 @@ x-pack/plugins/observability_solution/observability_shared/public/components/pro # Shared UX /x-pack/test/api_integration/apis/content_management @elastic/appex-sharedux +/x-pack/test/accessibility/apps/group3/tags.ts @elastic/appex-sharedux +/x-pack/test/accessibility/apps/group3/snapshot_and_restore.ts @elastic/appex-sharedux /x-pack/test_serverless/functional/test_suites/common/spaces/spaces_selection.ts @elastic/appex-sharedux /x-pack/test_serverless/functional/test_suites/common/spaces/index.ts @elastic/appex-sharedux packages/react @elastic/appex-sharedux diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index 5bd83634aa9fa..086dc91b415e8 100644 --- a/api_docs/actions.mdx +++ b/api_docs/actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/actions title: "actions" image: https://source.unsplash.com/400x175/?github description: API docs for the actions plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'actions'] --- import actionsObj from './actions.devdocs.json'; diff --git a/api_docs/advanced_settings.mdx b/api_docs/advanced_settings.mdx index 1ea53850e1add..094bdec905469 100644 --- a/api_docs/advanced_settings.mdx +++ b/api_docs/advanced_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/advancedSettings title: "advancedSettings" image: https://source.unsplash.com/400x175/?github description: API docs for the advancedSettings plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/ai_assistant_management_selection.mdx b/api_docs/ai_assistant_management_selection.mdx index f9264bcf8250d..41ac77f84104b 100644 --- a/api_docs/ai_assistant_management_selection.mdx +++ b/api_docs/ai_assistant_management_selection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiAssistantManagementSelection title: "aiAssistantManagementSelection" image: https://source.unsplash.com/400x175/?github description: API docs for the aiAssistantManagementSelection plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiAssistantManagementSelection'] --- import aiAssistantManagementSelectionObj from './ai_assistant_management_selection.devdocs.json'; diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index ad006a3891a9b..17d1ceee9d7ff 100644 --- a/api_docs/aiops.mdx +++ b/api_docs/aiops.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiops title: "aiops" image: https://source.unsplash.com/400x175/?github description: API docs for the aiops plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiops'] --- import aiopsObj from './aiops.devdocs.json'; diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index f4696a33fc90e..b83d5f9c750cc 100644 --- a/api_docs/alerting.mdx +++ b/api_docs/alerting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/alerting title: "alerting" image: https://source.unsplash.com/400x175/?github description: API docs for the alerting plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'alerting'] --- import alertingObj from './alerting.devdocs.json'; diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index 99b28298255a6..a112234127df2 100644 --- a/api_docs/apm.mdx +++ b/api_docs/apm.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apm title: "apm" image: https://source.unsplash.com/400x175/?github description: API docs for the apm plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/apm_data_access.mdx b/api_docs/apm_data_access.mdx index 48b39cdfa92c9..339ebaf4f5389 100644 --- a/api_docs/apm_data_access.mdx +++ b/api_docs/apm_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apmDataAccess title: "apmDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the apmDataAccess plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apmDataAccess'] --- import apmDataAccessObj from './apm_data_access.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index 04176d2453073..8d2ee365061c6 100644 --- a/api_docs/banners.mdx +++ b/api_docs/banners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/banners title: "banners" image: https://source.unsplash.com/400x175/?github description: API docs for the banners plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'banners'] --- import bannersObj from './banners.devdocs.json'; diff --git a/api_docs/bfetch.mdx b/api_docs/bfetch.mdx index 1591c7b31e45f..9662a90c22627 100644 --- a/api_docs/bfetch.mdx +++ b/api_docs/bfetch.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/bfetch title: "bfetch" image: https://source.unsplash.com/400x175/?github description: API docs for the bfetch plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'bfetch'] --- import bfetchObj from './bfetch.devdocs.json'; diff --git a/api_docs/canvas.mdx b/api_docs/canvas.mdx index 96d1e230442a5..433bd281157ca 100644 --- a/api_docs/canvas.mdx +++ b/api_docs/canvas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/canvas title: "canvas" image: https://source.unsplash.com/400x175/?github description: API docs for the canvas plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'canvas'] --- import canvasObj from './canvas.devdocs.json'; diff --git a/api_docs/cases.mdx b/api_docs/cases.mdx index 24c932d5de302..2213f517514f7 100644 --- a/api_docs/cases.mdx +++ b/api_docs/cases.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cases title: "cases" image: https://source.unsplash.com/400x175/?github description: API docs for the cases plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cases'] --- import casesObj from './cases.devdocs.json'; diff --git a/api_docs/charts.mdx b/api_docs/charts.mdx index a3c82675ac020..315f75c88443a 100644 --- a/api_docs/charts.mdx +++ b/api_docs/charts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/charts title: "charts" image: https://source.unsplash.com/400x175/?github description: API docs for the charts plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'charts'] --- import chartsObj from './charts.devdocs.json'; diff --git a/api_docs/cloud.mdx b/api_docs/cloud.mdx index b49f6ae3cd10a..536dd23ea5078 100644 --- a/api_docs/cloud.mdx +++ b/api_docs/cloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloud title: "cloud" image: https://source.unsplash.com/400x175/?github description: API docs for the cloud plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_data_migration.mdx b/api_docs/cloud_data_migration.mdx index 4922d2d17e5cc..36bec7ada0161 100644 --- a/api_docs/cloud_data_migration.mdx +++ b/api_docs/cloud_data_migration.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDataMigration title: "cloudDataMigration" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDataMigration plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDataMigration'] --- import cloudDataMigrationObj from './cloud_data_migration.devdocs.json'; diff --git a/api_docs/cloud_defend.mdx b/api_docs/cloud_defend.mdx index e8a297a0f251d..42a532fd51356 100644 --- a/api_docs/cloud_defend.mdx +++ b/api_docs/cloud_defend.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDefend title: "cloudDefend" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDefend plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDefend'] --- import cloudDefendObj from './cloud_defend.devdocs.json'; diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index 3536dcb32d915..de8694bc100a8 100644 --- a/api_docs/cloud_security_posture.mdx +++ b/api_docs/cloud_security_posture.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudSecurityPosture title: "cloudSecurityPosture" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudSecurityPosture plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudSecurityPosture'] --- import cloudSecurityPostureObj from './cloud_security_posture.devdocs.json'; diff --git a/api_docs/console.mdx b/api_docs/console.mdx index fb68e8a70e378..ab22dfa3eb524 100644 --- a/api_docs/console.mdx +++ b/api_docs/console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/console title: "console" image: https://source.unsplash.com/400x175/?github description: API docs for the console plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] --- import consoleObj from './console.devdocs.json'; diff --git a/api_docs/content_management.mdx b/api_docs/content_management.mdx index 3281128a5c67c..3e7ac9aefea9e 100644 --- a/api_docs/content_management.mdx +++ b/api_docs/content_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/contentManagement title: "contentManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the contentManagement plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'contentManagement'] --- import contentManagementObj from './content_management.devdocs.json'; diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index 83a06b887a4dd..ad588bedc7bcc 100644 --- a/api_docs/controls.mdx +++ b/api_docs/controls.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/controls title: "controls" image: https://source.unsplash.com/400x175/?github description: API docs for the controls plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] --- import controlsObj from './controls.devdocs.json'; diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index 64cec1c539f5b..241e70a6701f6 100644 --- a/api_docs/custom_integrations.mdx +++ b/api_docs/custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/customIntegrations title: "customIntegrations" image: https://source.unsplash.com/400x175/?github description: API docs for the customIntegrations plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'customIntegrations'] --- import customIntegrationsObj from './custom_integrations.devdocs.json'; diff --git a/api_docs/dashboard.mdx b/api_docs/dashboard.mdx index c9fba2b7c19f6..f296e4bcf45fe 100644 --- a/api_docs/dashboard.mdx +++ b/api_docs/dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboard title: "dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboard plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboard'] --- import dashboardObj from './dashboard.devdocs.json'; diff --git a/api_docs/dashboard_enhanced.mdx b/api_docs/dashboard_enhanced.mdx index 6a98eab7f4355..b6e253bdeac20 100644 --- a/api_docs/dashboard_enhanced.mdx +++ b/api_docs/dashboard_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboardEnhanced title: "dashboardEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboardEnhanced plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced'] --- import dashboardEnhancedObj from './dashboard_enhanced.devdocs.json'; diff --git a/api_docs/data.mdx b/api_docs/data.mdx index eab39219c372a..c2c1e120912d9 100644 --- a/api_docs/data.mdx +++ b/api_docs/data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data title: "data" image: https://source.unsplash.com/400x175/?github description: API docs for the data plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] --- import dataObj from './data.devdocs.json'; diff --git a/api_docs/data_quality.mdx b/api_docs/data_quality.mdx index 55172bb81524b..3c8a78f752e00 100644 --- a/api_docs/data_quality.mdx +++ b/api_docs/data_quality.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataQuality title: "dataQuality" image: https://source.unsplash.com/400x175/?github description: API docs for the dataQuality plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataQuality'] --- import dataQualityObj from './data_quality.devdocs.json'; diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index 91d9ea2ff99ab..47dfc97f200df 100644 --- a/api_docs/data_query.mdx +++ b/api_docs/data_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-query title: "data.query" image: https://source.unsplash.com/400x175/?github description: API docs for the data.query plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query'] --- import dataQueryObj from './data_query.devdocs.json'; diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 7737c6a20808c..a01ba05a09591 100644 --- a/api_docs/data_search.mdx +++ b/api_docs/data_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-search title: "data.search" image: https://source.unsplash.com/400x175/?github description: API docs for the data.search plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] --- import dataSearchObj from './data_search.devdocs.json'; diff --git a/api_docs/data_usage.mdx b/api_docs/data_usage.mdx index 649117c73f79d..b651ab5e4e5a0 100644 --- a/api_docs/data_usage.mdx +++ b/api_docs/data_usage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataUsage title: "dataUsage" image: https://source.unsplash.com/400x175/?github description: API docs for the dataUsage plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataUsage'] --- import dataUsageObj from './data_usage.devdocs.json'; diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index a560a29c61131..857413c5dc8eb 100644 --- a/api_docs/data_view_editor.mdx +++ b/api_docs/data_view_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewEditor title: "dataViewEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewEditor plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewEditor'] --- import dataViewEditorObj from './data_view_editor.devdocs.json'; diff --git a/api_docs/data_view_field_editor.mdx b/api_docs/data_view_field_editor.mdx index eb4ddeb297483..ad62738c38e82 100644 --- a/api_docs/data_view_field_editor.mdx +++ b/api_docs/data_view_field_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewFieldEditor title: "dataViewFieldEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewFieldEditor plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewFieldEditor'] --- import dataViewFieldEditorObj from './data_view_field_editor.devdocs.json'; diff --git a/api_docs/data_view_management.mdx b/api_docs/data_view_management.mdx index 5bcbbb88f3c89..ec29d2f07d6c2 100644 --- a/api_docs/data_view_management.mdx +++ b/api_docs/data_view_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewManagement title: "dataViewManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewManagement plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewManagement'] --- import dataViewManagementObj from './data_view_management.devdocs.json'; diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx index d0ffb52c5bba7..402b8ebb7d47c 100644 --- a/api_docs/data_views.mdx +++ b/api_docs/data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViews title: "dataViews" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViews plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViews'] --- import dataViewsObj from './data_views.devdocs.json'; diff --git a/api_docs/data_visualizer.mdx b/api_docs/data_visualizer.mdx index 39219e7d09dd5..6ef2c39902ba1 100644 --- a/api_docs/data_visualizer.mdx +++ b/api_docs/data_visualizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataVisualizer title: "dataVisualizer" image: https://source.unsplash.com/400x175/?github description: API docs for the dataVisualizer plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/dataset_quality.mdx b/api_docs/dataset_quality.mdx index 301fab88d2608..e1b82c70171f5 100644 --- a/api_docs/dataset_quality.mdx +++ b/api_docs/dataset_quality.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/datasetQuality title: "datasetQuality" image: https://source.unsplash.com/400x175/?github description: API docs for the datasetQuality plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'datasetQuality'] --- import datasetQualityObj from './dataset_quality.devdocs.json'; diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index d3a02c223ebcb..6e1af31607b8d 100644 --- a/api_docs/deprecations_by_api.mdx +++ b/api_docs/deprecations_by_api.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByApi slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-api title: Deprecated API usage by API description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 873bcdc3e2060..f5d2a6fcded41 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByPlugin slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-plugin title: Deprecated API usage by plugin description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index 5108d057e4a8b..720346e389d94 100644 --- a/api_docs/deprecations_by_team.mdx +++ b/api_docs/deprecations_by_team.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsDueByTeam slug: /kibana-dev-docs/api-meta/deprecations-due-by-team title: Deprecated APIs due to be removed, by team description: Lists the teams that are referencing deprecated APIs with a remove by date. -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index df66921effe99..b1d46f78733f6 100644 --- a/api_docs/dev_tools.mdx +++ b/api_docs/dev_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/devTools title: "devTools" image: https://source.unsplash.com/400x175/?github description: API docs for the devTools plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'devTools'] --- import devToolsObj from './dev_tools.devdocs.json'; diff --git a/api_docs/discover.mdx b/api_docs/discover.mdx index 27c3751485c04..35a9dc5f815e8 100644 --- a/api_docs/discover.mdx +++ b/api_docs/discover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discover title: "discover" image: https://source.unsplash.com/400x175/?github description: API docs for the discover plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discover'] --- import discoverObj from './discover.devdocs.json'; diff --git a/api_docs/discover_enhanced.mdx b/api_docs/discover_enhanced.mdx index 125847fb5e7d1..cdec938f3cdde 100644 --- a/api_docs/discover_enhanced.mdx +++ b/api_docs/discover_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverEnhanced title: "discoverEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverEnhanced plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/discover_shared.mdx b/api_docs/discover_shared.mdx index 32ce707d66c51..ed34c9b7d26b0 100644 --- a/api_docs/discover_shared.mdx +++ b/api_docs/discover_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverShared title: "discoverShared" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverShared plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverShared'] --- import discoverSharedObj from './discover_shared.devdocs.json'; diff --git a/api_docs/ecs_data_quality_dashboard.mdx b/api_docs/ecs_data_quality_dashboard.mdx index cf7c2a558e832..4642fd072ef04 100644 --- a/api_docs/ecs_data_quality_dashboard.mdx +++ b/api_docs/ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ecsDataQualityDashboard title: "ecsDataQualityDashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the ecsDataQualityDashboard plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ecsDataQualityDashboard'] --- import ecsDataQualityDashboardObj from './ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/elastic_assistant.mdx b/api_docs/elastic_assistant.mdx index c75e15ff98d67..5722396a73f0d 100644 --- a/api_docs/elastic_assistant.mdx +++ b/api_docs/elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/elasticAssistant title: "elasticAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the elasticAssistant plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'elasticAssistant'] --- import elasticAssistantObj from './elastic_assistant.devdocs.json'; diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index 1fbea31155fb7..4d2b6ae31ec33 100644 --- a/api_docs/embeddable.mdx +++ b/api_docs/embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddable title: "embeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddable plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddable'] --- import embeddableObj from './embeddable.devdocs.json'; diff --git a/api_docs/embeddable_enhanced.mdx b/api_docs/embeddable_enhanced.mdx index e7a5a083c0f64..01a48826572f0 100644 --- a/api_docs/embeddable_enhanced.mdx +++ b/api_docs/embeddable_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddableEnhanced title: "embeddableEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddableEnhanced plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddableEnhanced'] --- import embeddableEnhancedObj from './embeddable_enhanced.devdocs.json'; diff --git a/api_docs/encrypted_saved_objects.mdx b/api_docs/encrypted_saved_objects.mdx index b0689b74afdaa..0daf90c42d212 100644 --- a/api_docs/encrypted_saved_objects.mdx +++ b/api_docs/encrypted_saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/encryptedSavedObjects title: "encryptedSavedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the encryptedSavedObjects plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'encryptedSavedObjects'] --- import encryptedSavedObjectsObj from './encrypted_saved_objects.devdocs.json'; diff --git a/api_docs/enterprise_search.mdx b/api_docs/enterprise_search.mdx index e8c06fe565786..85bbe03fac19d 100644 --- a/api_docs/enterprise_search.mdx +++ b/api_docs/enterprise_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/enterpriseSearch title: "enterpriseSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the enterpriseSearch plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'enterpriseSearch'] --- import enterpriseSearchObj from './enterprise_search.devdocs.json'; diff --git a/api_docs/entities_data_access.mdx b/api_docs/entities_data_access.mdx index 621d002adc61e..8b5eb703ed019 100644 --- a/api_docs/entities_data_access.mdx +++ b/api_docs/entities_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/entitiesDataAccess title: "entitiesDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the entitiesDataAccess plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'entitiesDataAccess'] --- import entitiesDataAccessObj from './entities_data_access.devdocs.json'; diff --git a/api_docs/entity_manager.mdx b/api_docs/entity_manager.mdx index d1a758cf0cd53..838e66336d32a 100644 --- a/api_docs/entity_manager.mdx +++ b/api_docs/entity_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/entityManager title: "entityManager" image: https://source.unsplash.com/400x175/?github description: API docs for the entityManager plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'entityManager'] --- import entityManagerObj from './entity_manager.devdocs.json'; diff --git a/api_docs/es_ui_shared.mdx b/api_docs/es_ui_shared.mdx index b009b99297081..5c7b2c44d3ed6 100644 --- a/api_docs/es_ui_shared.mdx +++ b/api_docs/es_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esUiShared title: "esUiShared" image: https://source.unsplash.com/400x175/?github description: API docs for the esUiShared plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] --- import esUiSharedObj from './es_ui_shared.devdocs.json'; diff --git a/api_docs/esql.mdx b/api_docs/esql.mdx index 21c07bd4e8e74..cf32accf6abb8 100644 --- a/api_docs/esql.mdx +++ b/api_docs/esql.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esql title: "esql" image: https://source.unsplash.com/400x175/?github description: API docs for the esql plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esql'] --- import esqlObj from './esql.devdocs.json'; diff --git a/api_docs/esql_data_grid.mdx b/api_docs/esql_data_grid.mdx index 15e91bf6e8550..e2d1a850e9241 100644 --- a/api_docs/esql_data_grid.mdx +++ b/api_docs/esql_data_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esqlDataGrid title: "esqlDataGrid" image: https://source.unsplash.com/400x175/?github description: API docs for the esqlDataGrid plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esqlDataGrid'] --- import esqlDataGridObj from './esql_data_grid.devdocs.json'; diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index 3c9ab8175d1ec..ee07f7105352c 100644 --- a/api_docs/event_annotation.mdx +++ b/api_docs/event_annotation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotation title: "eventAnnotation" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotation plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; diff --git a/api_docs/event_annotation_listing.mdx b/api_docs/event_annotation_listing.mdx index b716960bb0cbb..5bbe6479f6116 100644 --- a/api_docs/event_annotation_listing.mdx +++ b/api_docs/event_annotation_listing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotationListing title: "eventAnnotationListing" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotationListing plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotationListing'] --- import eventAnnotationListingObj from './event_annotation_listing.devdocs.json'; diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index c6681f0448529..cd21281184379 100644 --- a/api_docs/event_log.mdx +++ b/api_docs/event_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventLog title: "eventLog" image: https://source.unsplash.com/400x175/?github description: API docs for the eventLog plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; diff --git a/api_docs/exploratory_view.mdx b/api_docs/exploratory_view.mdx index 31966b6415aa8..2bf13bbbe7dba 100644 --- a/api_docs/exploratory_view.mdx +++ b/api_docs/exploratory_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/exploratoryView title: "exploratoryView" image: https://source.unsplash.com/400x175/?github description: API docs for the exploratoryView plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'exploratoryView'] --- import exploratoryViewObj from './exploratory_view.devdocs.json'; diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index 90d5232c5565f..774a05f8f345e 100644 --- a/api_docs/expression_error.mdx +++ b/api_docs/expression_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionError title: "expressionError" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionError plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionError'] --- import expressionErrorObj from './expression_error.devdocs.json'; diff --git a/api_docs/expression_gauge.mdx b/api_docs/expression_gauge.mdx index 53aa52b8fa9c0..63c3151e8a1dd 100644 --- a/api_docs/expression_gauge.mdx +++ b/api_docs/expression_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionGauge title: "expressionGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionGauge plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionGauge'] --- import expressionGaugeObj from './expression_gauge.devdocs.json'; diff --git a/api_docs/expression_heatmap.mdx b/api_docs/expression_heatmap.mdx index d3d6f03b6acf8..eb18d1cac26ca 100644 --- a/api_docs/expression_heatmap.mdx +++ b/api_docs/expression_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionHeatmap title: "expressionHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionHeatmap plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionHeatmap'] --- import expressionHeatmapObj from './expression_heatmap.devdocs.json'; diff --git a/api_docs/expression_image.mdx b/api_docs/expression_image.mdx index e4745c8efc20e..7246080baa148 100644 --- a/api_docs/expression_image.mdx +++ b/api_docs/expression_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionImage title: "expressionImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionImage plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionImage'] --- import expressionImageObj from './expression_image.devdocs.json'; diff --git a/api_docs/expression_legacy_metric_vis.mdx b/api_docs/expression_legacy_metric_vis.mdx index 81a77b96d9d4b..1593bc4d7c1fe 100644 --- a/api_docs/expression_legacy_metric_vis.mdx +++ b/api_docs/expression_legacy_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionLegacyMetricVis title: "expressionLegacyMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionLegacyMetricVis plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionLegacyMetricVis'] --- import expressionLegacyMetricVisObj from './expression_legacy_metric_vis.devdocs.json'; diff --git a/api_docs/expression_metric.mdx b/api_docs/expression_metric.mdx index e816c6e1b59dc..0ad608ac54949 100644 --- a/api_docs/expression_metric.mdx +++ b/api_docs/expression_metric.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetric title: "expressionMetric" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetric plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetric'] --- import expressionMetricObj from './expression_metric.devdocs.json'; diff --git a/api_docs/expression_metric_vis.mdx b/api_docs/expression_metric_vis.mdx index f53a2fc8d7c91..99d176b11ea2b 100644 --- a/api_docs/expression_metric_vis.mdx +++ b/api_docs/expression_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetricVis title: "expressionMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetricVis plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetricVis'] --- import expressionMetricVisObj from './expression_metric_vis.devdocs.json'; diff --git a/api_docs/expression_partition_vis.mdx b/api_docs/expression_partition_vis.mdx index b05c0db6e3af2..e727fbb817adc 100644 --- a/api_docs/expression_partition_vis.mdx +++ b/api_docs/expression_partition_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionPartitionVis title: "expressionPartitionVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionPartitionVis plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionPartitionVis'] --- import expressionPartitionVisObj from './expression_partition_vis.devdocs.json'; diff --git a/api_docs/expression_repeat_image.mdx b/api_docs/expression_repeat_image.mdx index 8693bb98b3504..6235ea045c712 100644 --- a/api_docs/expression_repeat_image.mdx +++ b/api_docs/expression_repeat_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRepeatImage title: "expressionRepeatImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRepeatImage plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRepeatImage'] --- import expressionRepeatImageObj from './expression_repeat_image.devdocs.json'; diff --git a/api_docs/expression_reveal_image.mdx b/api_docs/expression_reveal_image.mdx index 41e84e01da888..205ba906c1fb1 100644 --- a/api_docs/expression_reveal_image.mdx +++ b/api_docs/expression_reveal_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRevealImage title: "expressionRevealImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRevealImage plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRevealImage'] --- import expressionRevealImageObj from './expression_reveal_image.devdocs.json'; diff --git a/api_docs/expression_shape.mdx b/api_docs/expression_shape.mdx index 615c8e8abe750..1528b031ff485 100644 --- a/api_docs/expression_shape.mdx +++ b/api_docs/expression_shape.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionShape title: "expressionShape" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionShape plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionShape'] --- import expressionShapeObj from './expression_shape.devdocs.json'; diff --git a/api_docs/expression_tagcloud.mdx b/api_docs/expression_tagcloud.mdx index 110aee9ad4a78..2d0dce1478f38 100644 --- a/api_docs/expression_tagcloud.mdx +++ b/api_docs/expression_tagcloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionTagcloud title: "expressionTagcloud" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionTagcloud plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] --- import expressionTagcloudObj from './expression_tagcloud.devdocs.json'; diff --git a/api_docs/expression_x_y.devdocs.json b/api_docs/expression_x_y.devdocs.json index 93f55a1f188fe..38e1d5af388f5 100644 --- a/api_docs/expression_x_y.devdocs.json +++ b/api_docs/expression_x_y.devdocs.json @@ -38,6 +38,21 @@ } ], "objects": [ + { + "parentPluginId": "expressionXY", + "id": "def-public.FittingFunctions", + "type": "Object", + "tags": [], + "label": "FittingFunctions", + "description": [], + "signature": [ + "{ readonly NONE: \"None\"; readonly ZERO: \"Zero\"; readonly LINEAR: \"Linear\"; readonly CARRY: \"Carry\"; readonly LOOKAHEAD: \"Lookahead\"; readonly AVERAGE: \"Average\"; readonly NEAREST: \"Nearest\"; }" + ], + "path": "src/plugins/chart_expressions/expression_xy/common/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "expressionXY", "id": "def-public.LayerTypes", @@ -2931,7 +2946,7 @@ "label": "FittingFunction", "description": [], "signature": [ - "\"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\"" + "\"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\"" ], "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts", "deprecated": false, @@ -3635,6 +3650,21 @@ } ], "objects": [ + { + "parentPluginId": "expressionXY", + "id": "def-common.FittingFunctions", + "type": "Object", + "tags": [], + "label": "FittingFunctions", + "description": [], + "signature": [ + "{ readonly NONE: \"None\"; readonly ZERO: \"Zero\"; readonly LINEAR: \"Linear\"; readonly CARRY: \"Carry\"; readonly LOOKAHEAD: \"Lookahead\"; readonly AVERAGE: \"Average\"; readonly NEAREST: \"Nearest\"; }" + ], + "path": "src/plugins/chart_expressions/expression_xy/common/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "expressionXY", "id": "def-common.LayerTypes", diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index ca232502d58f2..05039e00ed3f4 100644 --- a/api_docs/expression_x_y.mdx +++ b/api_docs/expression_x_y.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionXY title: "expressionXY" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionXY plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY'] --- import expressionXYObj from './expression_x_y.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 180 | 0 | 169 | 13 | +| 182 | 0 | 171 | 13 | ## Client diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx index 06ecd910dfeb4..6429e6986503f 100644 --- a/api_docs/expressions.mdx +++ b/api_docs/expressions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressions title: "expressions" image: https://source.unsplash.com/400x175/?github description: API docs for the expressions plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressions'] --- import expressionsObj from './expressions.devdocs.json'; diff --git a/api_docs/features.mdx b/api_docs/features.mdx index 6b6508ee562a4..d7bd3ec4f6b15 100644 --- a/api_docs/features.mdx +++ b/api_docs/features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/features title: "features" image: https://source.unsplash.com/400x175/?github description: API docs for the features plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'features'] --- import featuresObj from './features.devdocs.json'; diff --git a/api_docs/field_formats.mdx b/api_docs/field_formats.mdx index 0aefb39882ed6..ce6582accf7d8 100644 --- a/api_docs/field_formats.mdx +++ b/api_docs/field_formats.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fieldFormats title: "fieldFormats" image: https://source.unsplash.com/400x175/?github description: API docs for the fieldFormats plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldFormats'] --- import fieldFormatsObj from './field_formats.devdocs.json'; diff --git a/api_docs/fields_metadata.mdx b/api_docs/fields_metadata.mdx index 7a71db7c76e6b..3092178667566 100644 --- a/api_docs/fields_metadata.mdx +++ b/api_docs/fields_metadata.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fieldsMetadata title: "fieldsMetadata" image: https://source.unsplash.com/400x175/?github description: API docs for the fieldsMetadata plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldsMetadata'] --- import fieldsMetadataObj from './fields_metadata.devdocs.json'; diff --git a/api_docs/file_upload.mdx b/api_docs/file_upload.mdx index 49e79b28daf7b..e7084ec363d82 100644 --- a/api_docs/file_upload.mdx +++ b/api_docs/file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fileUpload title: "fileUpload" image: https://source.unsplash.com/400x175/?github description: API docs for the fileUpload plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fileUpload'] --- import fileUploadObj from './file_upload.devdocs.json'; diff --git a/api_docs/files.mdx b/api_docs/files.mdx index a40095d2c4852..341c9b85a3bee 100644 --- a/api_docs/files.mdx +++ b/api_docs/files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/files title: "files" image: https://source.unsplash.com/400x175/?github description: API docs for the files plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'files'] --- import filesObj from './files.devdocs.json'; diff --git a/api_docs/files_management.mdx b/api_docs/files_management.mdx index a9c30b11080fd..e6f7f9266ebe9 100644 --- a/api_docs/files_management.mdx +++ b/api_docs/files_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/filesManagement title: "filesManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the filesManagement plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'filesManagement'] --- import filesManagementObj from './files_management.devdocs.json'; diff --git a/api_docs/fleet.devdocs.json b/api_docs/fleet.devdocs.json index 009ef5bb74cf4..3ac36ec52b20b 100644 --- a/api_docs/fleet.devdocs.json +++ b/api_docs/fleet.devdocs.json @@ -22777,7 +22777,7 @@ "section": "def-common.ListWithKuery", "text": "ListWithKuery" }, - " & { noAgentCount?: boolean | undefined; full?: boolean | undefined; }" + " & { noAgentCount?: boolean | undefined; withAgentCount?: boolean | undefined; full?: boolean | undefined; }" ], "path": "x-pack/plugins/fleet/common/types/rest_spec/agent_policy.ts", "deprecated": false, @@ -29286,6 +29286,28 @@ "path": "x-pack/plugins/fleet/common/constants/routes.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "fleet", + "id": "def-common.AGENT_POLICY_API_ROUTES.LIST_OUTPUTS_PATTERN", + "type": "string", + "tags": [], + "label": "LIST_OUTPUTS_PATTERN", + "description": [], + "path": "x-pack/plugins/fleet/common/constants/routes.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "fleet", + "id": "def-common.AGENT_POLICY_API_ROUTES.INFO_OUTPUTS_PATTERN", + "type": "string", + "tags": [], + "label": "INFO_OUTPUTS_PATTERN", + "description": [], + "path": "x-pack/plugins/fleet/common/constants/routes.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -29604,6 +29626,54 @@ "trackAdoption": false, "children": [], "returnComment": [] + }, + { + "parentPluginId": "fleet", + "id": "def-common.agentPolicyRouteService.getInfoOutputsPath", + "type": "Function", + "tags": [], + "label": "getInfoOutputsPath", + "description": [], + "signature": [ + "(agentPolicyId: string) => string" + ], + "path": "x-pack/plugins/fleet/common/services/routes.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "fleet", + "id": "def-common.agentPolicyRouteService.getInfoOutputsPath.$1", + "type": "string", + "tags": [], + "label": "agentPolicyId", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/plugins/fleet/common/services/routes.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "fleet", + "id": "def-common.agentPolicyRouteService.getListOutputsPath", + "type": "Function", + "tags": [], + "label": "getListOutputsPath", + "description": [], + "signature": [ + "() => string" + ], + "path": "x-pack/plugins/fleet/common/services/routes.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] } ], "initialIsOpen": false diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index f14348c9c601a..548b460dc8ee1 100644 --- a/api_docs/fleet.mdx +++ b/api_docs/fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fleet title: "fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the fleet plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] --- import fleetObj from './fleet.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) for questi | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 1421 | 5 | 1296 | 76 | +| 1426 | 5 | 1301 | 76 | ## Client diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index b87aad57a8b44..f37e9ce8ff244 100644 --- a/api_docs/global_search.mdx +++ b/api_docs/global_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/globalSearch title: "globalSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the globalSearch plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'globalSearch'] --- import globalSearchObj from './global_search.devdocs.json'; diff --git a/api_docs/guided_onboarding.mdx b/api_docs/guided_onboarding.mdx index 8e0192d16c372..da8b599277ae8 100644 --- a/api_docs/guided_onboarding.mdx +++ b/api_docs/guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/guidedOnboarding title: "guidedOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the guidedOnboarding plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'guidedOnboarding'] --- import guidedOnboardingObj from './guided_onboarding.devdocs.json'; diff --git a/api_docs/home.mdx b/api_docs/home.mdx index 02b148d214fc2..f01667c56b793 100644 --- a/api_docs/home.mdx +++ b/api_docs/home.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/home title: "home" image: https://source.unsplash.com/400x175/?github description: API docs for the home plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'home'] --- import homeObj from './home.devdocs.json'; diff --git a/api_docs/image_embeddable.mdx b/api_docs/image_embeddable.mdx index 1e4de9367402d..4fb826bd28a4c 100644 --- a/api_docs/image_embeddable.mdx +++ b/api_docs/image_embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/imageEmbeddable title: "imageEmbeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the imageEmbeddable plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'imageEmbeddable'] --- import imageEmbeddableObj from './image_embeddable.devdocs.json'; diff --git a/api_docs/index_lifecycle_management.mdx b/api_docs/index_lifecycle_management.mdx index 78453c3cb2a9d..9e826a040da2f 100644 --- a/api_docs/index_lifecycle_management.mdx +++ b/api_docs/index_lifecycle_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexLifecycleManagement title: "indexLifecycleManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexLifecycleManagement plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexLifecycleManagement'] --- import indexLifecycleManagementObj from './index_lifecycle_management.devdocs.json'; diff --git a/api_docs/index_management.mdx b/api_docs/index_management.mdx index b5c5d7eaca871..12bf1efb4c41f 100644 --- a/api_docs/index_management.mdx +++ b/api_docs/index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexManagement title: "indexManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexManagement plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] --- import indexManagementObj from './index_management.devdocs.json'; diff --git a/api_docs/inference.mdx b/api_docs/inference.mdx index a25c24caec01b..2cc82bfbeeae7 100644 --- a/api_docs/inference.mdx +++ b/api_docs/inference.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inference title: "inference" image: https://source.unsplash.com/400x175/?github description: API docs for the inference plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inference'] --- import inferenceObj from './inference.devdocs.json'; diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index ddd2eff52f8b4..b02e0387ea23a 100644 --- a/api_docs/infra.mdx +++ b/api_docs/infra.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/infra title: "infra" image: https://source.unsplash.com/400x175/?github description: API docs for the infra plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/ingest_pipelines.mdx b/api_docs/ingest_pipelines.mdx index 7926fae14dad0..281f262540933 100644 --- a/api_docs/ingest_pipelines.mdx +++ b/api_docs/ingest_pipelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ingestPipelines title: "ingestPipelines" image: https://source.unsplash.com/400x175/?github description: API docs for the ingestPipelines plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ingestPipelines'] --- import ingestPipelinesObj from './ingest_pipelines.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index 67ce9a1b4ac27..808171a931816 100644 --- a/api_docs/inspector.mdx +++ b/api_docs/inspector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inspector title: "inspector" image: https://source.unsplash.com/400x175/?github description: API docs for the inspector plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector'] --- import inspectorObj from './inspector.devdocs.json'; diff --git a/api_docs/integration_assistant.mdx b/api_docs/integration_assistant.mdx index ed622923791e1..c889bc987bf9f 100644 --- a/api_docs/integration_assistant.mdx +++ b/api_docs/integration_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/integrationAssistant title: "integrationAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the integrationAssistant plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'integrationAssistant'] --- import integrationAssistantObj from './integration_assistant.devdocs.json'; diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index 030bdf5a6c8b9..8089b4ecf472f 100644 --- a/api_docs/interactive_setup.mdx +++ b/api_docs/interactive_setup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/interactiveSetup title: "interactiveSetup" image: https://source.unsplash.com/400x175/?github description: API docs for the interactiveSetup plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup'] --- import interactiveSetupObj from './interactive_setup.devdocs.json'; diff --git a/api_docs/inventory.mdx b/api_docs/inventory.mdx index ea82f1498fe79..cd964bb303af5 100644 --- a/api_docs/inventory.mdx +++ b/api_docs/inventory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inventory title: "inventory" image: https://source.unsplash.com/400x175/?github description: API docs for the inventory plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inventory'] --- import inventoryObj from './inventory.devdocs.json'; diff --git a/api_docs/investigate.mdx b/api_docs/investigate.mdx index fa3722718ab91..b9434b646b026 100644 --- a/api_docs/investigate.mdx +++ b/api_docs/investigate.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/investigate title: "investigate" image: https://source.unsplash.com/400x175/?github description: API docs for the investigate plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'investigate'] --- import investigateObj from './investigate.devdocs.json'; diff --git a/api_docs/investigate_app.mdx b/api_docs/investigate_app.mdx index 28924d9f4facd..7f4ad6b2996d3 100644 --- a/api_docs/investigate_app.mdx +++ b/api_docs/investigate_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/investigateApp title: "investigateApp" image: https://source.unsplash.com/400x175/?github description: API docs for the investigateApp plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'investigateApp'] --- import investigateAppObj from './investigate_app.devdocs.json'; diff --git a/api_docs/kbn_actions_types.mdx b/api_docs/kbn_actions_types.mdx index 251bb9870cdca..52a3ccc639439 100644 --- a/api_docs/kbn_actions_types.mdx +++ b/api_docs/kbn_actions_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-actions-types title: "@kbn/actions-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/actions-types plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/actions-types'] --- import kbnActionsTypesObj from './kbn_actions_types.devdocs.json'; diff --git a/api_docs/kbn_ai_assistant.mdx b/api_docs/kbn_ai_assistant.mdx index 342a5fad58f96..c164f3a2f7b56 100644 --- a/api_docs/kbn_ai_assistant.mdx +++ b/api_docs/kbn_ai_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ai-assistant title: "@kbn/ai-assistant" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ai-assistant plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ai-assistant'] --- import kbnAiAssistantObj from './kbn_ai_assistant.devdocs.json'; diff --git a/api_docs/kbn_ai_assistant_common.mdx b/api_docs/kbn_ai_assistant_common.mdx index b75171bb0191c..70642bebc98bc 100644 --- a/api_docs/kbn_ai_assistant_common.mdx +++ b/api_docs/kbn_ai_assistant_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ai-assistant-common title: "@kbn/ai-assistant-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ai-assistant-common plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ai-assistant-common'] --- import kbnAiAssistantCommonObj from './kbn_ai_assistant_common.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index 67d65b437def2..93e4da1c70d10 100644 --- a/api_docs/kbn_aiops_components.mdx +++ b/api_docs/kbn_aiops_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-components title: "@kbn/aiops-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-components plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_log_pattern_analysis.mdx b/api_docs/kbn_aiops_log_pattern_analysis.mdx index b656af18160ee..f7b43cb4f8f2c 100644 --- a/api_docs/kbn_aiops_log_pattern_analysis.mdx +++ b/api_docs/kbn_aiops_log_pattern_analysis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-log-pattern-analysis title: "@kbn/aiops-log-pattern-analysis" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-log-pattern-analysis plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-log-pattern-analysis'] --- import kbnAiopsLogPatternAnalysisObj from './kbn_aiops_log_pattern_analysis.devdocs.json'; diff --git a/api_docs/kbn_aiops_log_rate_analysis.mdx b/api_docs/kbn_aiops_log_rate_analysis.mdx index c52f818cb75ca..d2b7ee5839ea3 100644 --- a/api_docs/kbn_aiops_log_rate_analysis.mdx +++ b/api_docs/kbn_aiops_log_rate_analysis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-log-rate-analysis title: "@kbn/aiops-log-rate-analysis" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-log-rate-analysis plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-log-rate-analysis'] --- import kbnAiopsLogRateAnalysisObj from './kbn_aiops_log_rate_analysis.devdocs.json'; diff --git a/api_docs/kbn_alerting_api_integration_helpers.mdx b/api_docs/kbn_alerting_api_integration_helpers.mdx index 97e329959c60e..e383c0fc293b7 100644 --- a/api_docs/kbn_alerting_api_integration_helpers.mdx +++ b/api_docs/kbn_alerting_api_integration_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-api-integration-helpers title: "@kbn/alerting-api-integration-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-api-integration-helpers plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-api-integration-helpers'] --- import kbnAlertingApiIntegrationHelpersObj from './kbn_alerting_api_integration_helpers.devdocs.json'; diff --git a/api_docs/kbn_alerting_comparators.mdx b/api_docs/kbn_alerting_comparators.mdx index 6854b19d705d9..1481276f9bb35 100644 --- a/api_docs/kbn_alerting_comparators.mdx +++ b/api_docs/kbn_alerting_comparators.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-comparators title: "@kbn/alerting-comparators" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-comparators plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-comparators'] --- import kbnAlertingComparatorsObj from './kbn_alerting_comparators.devdocs.json'; diff --git a/api_docs/kbn_alerting_state_types.mdx b/api_docs/kbn_alerting_state_types.mdx index b4113f1ca361d..8490ebf322821 100644 --- a/api_docs/kbn_alerting_state_types.mdx +++ b/api_docs/kbn_alerting_state_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-state-types title: "@kbn/alerting-state-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-state-types plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-state-types'] --- import kbnAlertingStateTypesObj from './kbn_alerting_state_types.devdocs.json'; diff --git a/api_docs/kbn_alerting_types.mdx b/api_docs/kbn_alerting_types.mdx index 10177db59a269..784684738e7bc 100644 --- a/api_docs/kbn_alerting_types.mdx +++ b/api_docs/kbn_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-types title: "@kbn/alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-types plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-types'] --- import kbnAlertingTypesObj from './kbn_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_alerts_as_data_utils.mdx b/api_docs/kbn_alerts_as_data_utils.mdx index de68b08c8a745..0e2b05414d1b9 100644 --- a/api_docs/kbn_alerts_as_data_utils.mdx +++ b/api_docs/kbn_alerts_as_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-as-data-utils title: "@kbn/alerts-as-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-as-data-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-as-data-utils'] --- import kbnAlertsAsDataUtilsObj from './kbn_alerts_as_data_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts_grouping.mdx b/api_docs/kbn_alerts_grouping.mdx index fd6518795e0bb..14216cf909bb2 100644 --- a/api_docs/kbn_alerts_grouping.mdx +++ b/api_docs/kbn_alerts_grouping.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-grouping title: "@kbn/alerts-grouping" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-grouping plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-grouping'] --- import kbnAlertsGroupingObj from './kbn_alerts_grouping.devdocs.json'; diff --git a/api_docs/kbn_alerts_ui_shared.mdx b/api_docs/kbn_alerts_ui_shared.mdx index 1457b615f123b..66459f898286c 100644 --- a/api_docs/kbn_alerts_ui_shared.mdx +++ b/api_docs/kbn_alerts_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-ui-shared title: "@kbn/alerts-ui-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-ui-shared plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-ui-shared'] --- import kbnAlertsUiSharedObj from './kbn_alerts_ui_shared.devdocs.json'; diff --git a/api_docs/kbn_analytics.mdx b/api_docs/kbn_analytics.mdx index f11a8ea2f136b..005033189b46b 100644 --- a/api_docs/kbn_analytics.mdx +++ b/api_docs/kbn_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics title: "@kbn/analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_collection_utils.mdx b/api_docs/kbn_analytics_collection_utils.mdx index 56fdea8dec13b..0d6f7c704ebbd 100644 --- a/api_docs/kbn_analytics_collection_utils.mdx +++ b/api_docs/kbn_analytics_collection_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-collection-utils title: "@kbn/analytics-collection-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-collection-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-collection-utils'] --- import kbnAnalyticsCollectionUtilsObj from './kbn_analytics_collection_utils.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index edd2af6aff97b..6cb8f7e168958 100644 --- a/api_docs/kbn_apm_config_loader.mdx +++ b/api_docs/kbn_apm_config_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-config-loader title: "@kbn/apm-config-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-config-loader plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-config-loader'] --- import kbnApmConfigLoaderObj from './kbn_apm_config_loader.devdocs.json'; diff --git a/api_docs/kbn_apm_data_view.mdx b/api_docs/kbn_apm_data_view.mdx index 936779e3623fe..9fbe9d77ce52a 100644 --- a/api_docs/kbn_apm_data_view.mdx +++ b/api_docs/kbn_apm_data_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-data-view title: "@kbn/apm-data-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-data-view plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-data-view'] --- import kbnApmDataViewObj from './kbn_apm_data_view.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index 61b9bd468ceff..4e17f1ae4ff8a 100644 --- a/api_docs/kbn_apm_synthtrace.mdx +++ b/api_docs/kbn_apm_synthtrace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace title: "@kbn/apm-synthtrace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace'] --- import kbnApmSynthtraceObj from './kbn_apm_synthtrace.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace_client.devdocs.json b/api_docs/kbn_apm_synthtrace_client.devdocs.json index 31e48f7188cfe..161efd033eb50 100644 --- a/api_docs/kbn_apm_synthtrace_client.devdocs.json +++ b/api_docs/kbn_apm_synthtrace_client.devdocs.json @@ -3476,6 +3476,379 @@ "trackAdoption": false } ] + }, + { + "parentPluginId": "@kbn/apm-synthtrace-client", + "id": "def-common.entities.k8s", + "type": "Object", + "tags": [], + "label": "k8s", + "description": [], + "path": "packages/kbn-apm-synthtrace-client/src/lib/entities/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/apm-synthtrace-client", + "id": "def-common.entities.k8s.k8sClusterJobEntity", + "type": "Function", + "tags": [], + "label": "k8sClusterJobEntity", + "description": [], + "signature": [ + "({ schema, name, entityId, ...others }: { [key: string]: any; schema: ", + "Schema", + "; name: string; entityId: string; }) => ", + "K8sEntity" + ], + "path": "packages/kbn-apm-synthtrace-client/src/lib/entities/index.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/apm-synthtrace-client", + "id": "def-common.entities.k8s.k8sClusterJobEntity.$1", + "type": "Object", + "tags": [], + "label": "__0", + "description": [], + "signature": [ + "{ [key: string]: any; schema: ", + "Schema", + "; name: string; entityId: string; }" + ], + "path": "packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/cluster_entity.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/apm-synthtrace-client", + "id": "def-common.entities.k8s.k8sCronJobEntity", + "type": "Function", + "tags": [], + "label": "k8sCronJobEntity", + "description": [], + "signature": [ + "({ schema, name, uid, clusterName, entityId, ...others }: { [key: string]: any; schema: ", + "Schema", + "; name: string; uid?: string | undefined; clusterName?: string | undefined; entityId: string; }) => ", + "K8sEntity" + ], + "path": "packages/kbn-apm-synthtrace-client/src/lib/entities/index.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/apm-synthtrace-client", + "id": "def-common.entities.k8s.k8sCronJobEntity.$1", + "type": "Object", + "tags": [], + "label": "__0", + "description": [], + "signature": [ + "{ [key: string]: any; schema: ", + "Schema", + "; name: string; uid?: string | undefined; clusterName?: string | undefined; entityId: string; }" + ], + "path": "packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/cron_job_entity.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/apm-synthtrace-client", + "id": "def-common.entities.k8s.k8sDaemonSetEntity", + "type": "Function", + "tags": [], + "label": "k8sDaemonSetEntity", + "description": [], + "signature": [ + "({ schema, name, uid, clusterName, entityId, ...others }: { [key: string]: any; schema: ", + "Schema", + "; name: string; uid?: string | undefined; clusterName?: string | undefined; entityId: string; }) => ", + "K8sEntity" + ], + "path": "packages/kbn-apm-synthtrace-client/src/lib/entities/index.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/apm-synthtrace-client", + "id": "def-common.entities.k8s.k8sDaemonSetEntity.$1", + "type": "Object", + "tags": [], + "label": "__0", + "description": [], + "signature": [ + "{ [key: string]: any; schema: ", + "Schema", + "; name: string; uid?: string | undefined; clusterName?: string | undefined; entityId: string; }" + ], + "path": "packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/daemon_set_entity.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/apm-synthtrace-client", + "id": "def-common.entities.k8s.k8sDeploymentEntity", + "type": "Function", + "tags": [], + "label": "k8sDeploymentEntity", + "description": [], + "signature": [ + "({ schema, name, uid, clusterName, entityId, ...others }: { [key: string]: any; schema: ", + "Schema", + "; name: string; uid?: string | undefined; clusterName?: string | undefined; entityId: string; }) => ", + "K8sEntity" + ], + "path": "packages/kbn-apm-synthtrace-client/src/lib/entities/index.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/apm-synthtrace-client", + "id": "def-common.entities.k8s.k8sDeploymentEntity.$1", + "type": "Object", + "tags": [], + "label": "__0", + "description": [], + "signature": [ + "{ [key: string]: any; schema: ", + "Schema", + "; name: string; uid?: string | undefined; clusterName?: string | undefined; entityId: string; }" + ], + "path": "packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/deployment_entity.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/apm-synthtrace-client", + "id": "def-common.entities.k8s.k8sJobSetEntity", + "type": "Function", + "tags": [], + "label": "k8sJobSetEntity", + "description": [], + "signature": [ + "({ schema, name, uid, clusterName, entityId, ...others }: { [key: string]: any; schema: ", + "Schema", + "; name: string; uid?: string | undefined; clusterName?: string | undefined; entityId: string; }) => ", + "K8sEntity" + ], + "path": "packages/kbn-apm-synthtrace-client/src/lib/entities/index.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/apm-synthtrace-client", + "id": "def-common.entities.k8s.k8sJobSetEntity.$1", + "type": "Object", + "tags": [], + "label": "__0", + "description": [], + "signature": [ + "{ [key: string]: any; schema: ", + "Schema", + "; name: string; uid?: string | undefined; clusterName?: string | undefined; entityId: string; }" + ], + "path": "packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/job_set_entity.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/apm-synthtrace-client", + "id": "def-common.entities.k8s.k8sNodeEntity", + "type": "Function", + "tags": [], + "label": "k8sNodeEntity", + "description": [], + "signature": [ + "({ schema, name, uid, clusterName, entityId, ...others }: { [key: string]: any; schema: ", + "Schema", + "; name: string; uid?: string | undefined; clusterName?: string | undefined; entityId: string; }) => ", + "K8sEntity" + ], + "path": "packages/kbn-apm-synthtrace-client/src/lib/entities/index.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/apm-synthtrace-client", + "id": "def-common.entities.k8s.k8sNodeEntity.$1", + "type": "Object", + "tags": [], + "label": "__0", + "description": [], + "signature": [ + "{ [key: string]: any; schema: ", + "Schema", + "; name: string; uid?: string | undefined; clusterName?: string | undefined; entityId: string; }" + ], + "path": "packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/node_entity.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/apm-synthtrace-client", + "id": "def-common.entities.k8s.k8sPodEntity", + "type": "Function", + "tags": [], + "label": "k8sPodEntity", + "description": [], + "signature": [ + "({ schema, name, uid, clusterName, entityId, ...others }: { [key: string]: any; schema: ", + "Schema", + "; name: string; uid?: string | undefined; clusterName?: string | undefined; entityId: string; }) => ", + "K8sEntity" + ], + "path": "packages/kbn-apm-synthtrace-client/src/lib/entities/index.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/apm-synthtrace-client", + "id": "def-common.entities.k8s.k8sPodEntity.$1", + "type": "Object", + "tags": [], + "label": "__0", + "description": [], + "signature": [ + "{ [key: string]: any; schema: ", + "Schema", + "; name: string; uid?: string | undefined; clusterName?: string | undefined; entityId: string; }" + ], + "path": "packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/pod_entity.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/apm-synthtrace-client", + "id": "def-common.entities.k8s.k8sReplicaSetEntity", + "type": "Function", + "tags": [], + "label": "k8sReplicaSetEntity", + "description": [], + "signature": [ + "({ schema, name, uid, clusterName, entityId, ...others }: { [key: string]: any; schema: ", + "Schema", + "; name: string; uid?: string | undefined; clusterName?: string | undefined; entityId: string; }) => ", + "K8sEntity" + ], + "path": "packages/kbn-apm-synthtrace-client/src/lib/entities/index.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/apm-synthtrace-client", + "id": "def-common.entities.k8s.k8sReplicaSetEntity.$1", + "type": "Object", + "tags": [], + "label": "__0", + "description": [], + "signature": [ + "{ [key: string]: any; schema: ", + "Schema", + "; name: string; uid?: string | undefined; clusterName?: string | undefined; entityId: string; }" + ], + "path": "packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/replica_set.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/apm-synthtrace-client", + "id": "def-common.entities.k8s.k8sStatefulSetEntity", + "type": "Function", + "tags": [], + "label": "k8sStatefulSetEntity", + "description": [], + "signature": [ + "({ schema, name, uid, clusterName, entityId, ...others }: { [key: string]: any; schema: ", + "Schema", + "; name: string; uid?: string | undefined; clusterName?: string | undefined; entityId: string; }) => ", + "K8sEntity" + ], + "path": "packages/kbn-apm-synthtrace-client/src/lib/entities/index.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/apm-synthtrace-client", + "id": "def-common.entities.k8s.k8sStatefulSetEntity.$1", + "type": "Object", + "tags": [], + "label": "__0", + "description": [], + "signature": [ + "{ [key: string]: any; schema: ", + "Schema", + "; name: string; uid?: string | undefined; clusterName?: string | undefined; entityId: string; }" + ], + "path": "packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/stateful_set.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/apm-synthtrace-client", + "id": "def-common.entities.k8s.k8sContainerEntity", + "type": "Function", + "tags": [], + "label": "k8sContainerEntity", + "description": [], + "signature": [ + "({ schema, id, entityId, ...others }: { [key: string]: any; schema: ", + "Schema", + "; id: string; entityId: string; }) => ", + "K8sEntity" + ], + "path": "packages/kbn-apm-synthtrace-client/src/lib/entities/index.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/apm-synthtrace-client", + "id": "def-common.entities.k8s.k8sContainerEntity.$1", + "type": "Object", + "tags": [], + "label": "__0", + "description": [], + "signature": [ + "{ [key: string]: any; schema: ", + "Schema", + "; id: string; entityId: string; }" + ], + "path": "packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/container_entity.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ] } ], "initialIsOpen": false diff --git a/api_docs/kbn_apm_synthtrace_client.mdx b/api_docs/kbn_apm_synthtrace_client.mdx index e96e12c6fbfed..444d4579c1bd5 100644 --- a/api_docs/kbn_apm_synthtrace_client.mdx +++ b/api_docs/kbn_apm_synthtrace_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace-client title: "@kbn/apm-synthtrace-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace-client plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace-client'] --- import kbnApmSynthtraceClientObj from './kbn_apm_synthtrace_client.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/te | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 247 | 0 | 247 | 36 | +| 268 | 0 | 268 | 38 | ## Common diff --git a/api_docs/kbn_apm_types.mdx b/api_docs/kbn_apm_types.mdx index 0079355996e4d..58d5838d248af 100644 --- a/api_docs/kbn_apm_types.mdx +++ b/api_docs/kbn_apm_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-types title: "@kbn/apm-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-types plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-types'] --- import kbnApmTypesObj from './kbn_apm_types.devdocs.json'; diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index 4065df83d8ad0..ef4dc7d1f8a0a 100644 --- a/api_docs/kbn_apm_utils.mdx +++ b/api_docs/kbn_apm_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-utils title: "@kbn/apm-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils'] --- import kbnApmUtilsObj from './kbn_apm_utils.devdocs.json'; diff --git a/api_docs/kbn_avc_banner.mdx b/api_docs/kbn_avc_banner.mdx index c76846ffb50a9..e2322763b738f 100644 --- a/api_docs/kbn_avc_banner.mdx +++ b/api_docs/kbn_avc_banner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-avc-banner title: "@kbn/avc-banner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/avc-banner plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/avc-banner'] --- import kbnAvcBannerObj from './kbn_avc_banner.devdocs.json'; diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx index 976a65e2013f6..0c9c9de31eb39 100644 --- a/api_docs/kbn_axe_config.mdx +++ b/api_docs/kbn_axe_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-axe-config title: "@kbn/axe-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/axe-config plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_bfetch_error.mdx b/api_docs/kbn_bfetch_error.mdx index 771d4c9405a34..7984983799ba7 100644 --- a/api_docs/kbn_bfetch_error.mdx +++ b/api_docs/kbn_bfetch_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-bfetch-error title: "@kbn/bfetch-error" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/bfetch-error plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/bfetch-error'] --- import kbnBfetchErrorObj from './kbn_bfetch_error.devdocs.json'; diff --git a/api_docs/kbn_calculate_auto.mdx b/api_docs/kbn_calculate_auto.mdx index a07fe789e7fd6..4ddcd3da9dd18 100644 --- a/api_docs/kbn_calculate_auto.mdx +++ b/api_docs/kbn_calculate_auto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-calculate-auto title: "@kbn/calculate-auto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/calculate-auto plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/calculate-auto'] --- import kbnCalculateAutoObj from './kbn_calculate_auto.devdocs.json'; diff --git a/api_docs/kbn_calculate_width_from_char_count.mdx b/api_docs/kbn_calculate_width_from_char_count.mdx index f485083db7ca6..3a619033e742c 100644 --- a/api_docs/kbn_calculate_width_from_char_count.mdx +++ b/api_docs/kbn_calculate_width_from_char_count.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-calculate-width-from-char-count title: "@kbn/calculate-width-from-char-count" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/calculate-width-from-char-count plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/calculate-width-from-char-count'] --- import kbnCalculateWidthFromCharCountObj from './kbn_calculate_width_from_char_count.devdocs.json'; diff --git a/api_docs/kbn_cases_components.mdx b/api_docs/kbn_cases_components.mdx index 0f3ae17d7b061..0b375644eccb8 100644 --- a/api_docs/kbn_cases_components.mdx +++ b/api_docs/kbn_cases_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cases-components title: "@kbn/cases-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cases-components plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cases-components'] --- import kbnCasesComponentsObj from './kbn_cases_components.devdocs.json'; diff --git a/api_docs/kbn_cbor.mdx b/api_docs/kbn_cbor.mdx index 38792fb0c0d21..36f664b205ddf 100644 --- a/api_docs/kbn_cbor.mdx +++ b/api_docs/kbn_cbor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cbor title: "@kbn/cbor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cbor plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cbor'] --- import kbnCborObj from './kbn_cbor.devdocs.json'; diff --git a/api_docs/kbn_cell_actions.mdx b/api_docs/kbn_cell_actions.mdx index 4c7a142df06d8..68d8a9ee88055 100644 --- a/api_docs/kbn_cell_actions.mdx +++ b/api_docs/kbn_cell_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cell-actions title: "@kbn/cell-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cell-actions plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cell-actions'] --- import kbnCellActionsObj from './kbn_cell_actions.devdocs.json'; diff --git a/api_docs/kbn_chart_expressions_common.mdx b/api_docs/kbn_chart_expressions_common.mdx index 726c05f020dc9..90d4a343a24cb 100644 --- a/api_docs/kbn_chart_expressions_common.mdx +++ b/api_docs/kbn_chart_expressions_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-expressions-common title: "@kbn/chart-expressions-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-expressions-common plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/chart-expressions-common'] --- import kbnChartExpressionsCommonObj from './kbn_chart_expressions_common.devdocs.json'; diff --git a/api_docs/kbn_chart_icons.mdx b/api_docs/kbn_chart_icons.mdx index 8cc92c932651a..7d8f5dbf2a5bb 100644 --- a/api_docs/kbn_chart_icons.mdx +++ b/api_docs/kbn_chart_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-icons title: "@kbn/chart-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-icons plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/chart-icons'] --- import kbnChartIconsObj from './kbn_chart_icons.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_core.mdx b/api_docs/kbn_ci_stats_core.mdx index dc80c70c56681..db1ea2a5fbd93 100644 --- a/api_docs/kbn_ci_stats_core.mdx +++ b/api_docs/kbn_ci_stats_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-core title: "@kbn/ci-stats-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-core plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-core'] --- import kbnCiStatsCoreObj from './kbn_ci_stats_core.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_performance_metrics.mdx b/api_docs/kbn_ci_stats_performance_metrics.mdx index 8360eac982cbb..5e13f5fd5f252 100644 --- a/api_docs/kbn_ci_stats_performance_metrics.mdx +++ b/api_docs/kbn_ci_stats_performance_metrics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-performance-metrics title: "@kbn/ci-stats-performance-metrics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-performance-metrics plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-performance-metrics'] --- import kbnCiStatsPerformanceMetricsObj from './kbn_ci_stats_performance_metrics.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_reporter.mdx b/api_docs/kbn_ci_stats_reporter.mdx index 49a1ebfb4619d..3dcba3792b199 100644 --- a/api_docs/kbn_ci_stats_reporter.mdx +++ b/api_docs/kbn_ci_stats_reporter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-reporter title: "@kbn/ci-stats-reporter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-reporter plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-reporter'] --- import kbnCiStatsReporterObj from './kbn_ci_stats_reporter.devdocs.json'; diff --git a/api_docs/kbn_cli_dev_mode.mdx b/api_docs/kbn_cli_dev_mode.mdx index 6bc2ebeee8749..b419f3a6783ab 100644 --- a/api_docs/kbn_cli_dev_mode.mdx +++ b/api_docs/kbn_cli_dev_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cli-dev-mode title: "@kbn/cli-dev-mode" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cli-dev-mode plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] --- import kbnCliDevModeObj from './kbn_cli_dev_mode.devdocs.json'; diff --git a/api_docs/kbn_cloud_security_posture.mdx b/api_docs/kbn_cloud_security_posture.mdx index f21e64b5b3a36..3bfa05c7c9339 100644 --- a/api_docs/kbn_cloud_security_posture.mdx +++ b/api_docs/kbn_cloud_security_posture.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cloud-security-posture title: "@kbn/cloud-security-posture" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cloud-security-posture plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cloud-security-posture'] --- import kbnCloudSecurityPostureObj from './kbn_cloud_security_posture.devdocs.json'; diff --git a/api_docs/kbn_cloud_security_posture_common.mdx b/api_docs/kbn_cloud_security_posture_common.mdx index a1872c4b1b950..f8e08d30d6491 100644 --- a/api_docs/kbn_cloud_security_posture_common.mdx +++ b/api_docs/kbn_cloud_security_posture_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cloud-security-posture-common title: "@kbn/cloud-security-posture-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cloud-security-posture-common plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cloud-security-posture-common'] --- import kbnCloudSecurityPostureCommonObj from './kbn_cloud_security_posture_common.devdocs.json'; diff --git a/api_docs/kbn_code_editor.mdx b/api_docs/kbn_code_editor.mdx index 5fa7f75460803..0877ddd2eea25 100644 --- a/api_docs/kbn_code_editor.mdx +++ b/api_docs/kbn_code_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor title: "@kbn/code-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor'] --- import kbnCodeEditorObj from './kbn_code_editor.devdocs.json'; diff --git a/api_docs/kbn_code_editor_mock.mdx b/api_docs/kbn_code_editor_mock.mdx index 1b9714f140829..882fb4ead4615 100644 --- a/api_docs/kbn_code_editor_mock.mdx +++ b/api_docs/kbn_code_editor_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor-mock title: "@kbn/code-editor-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor-mock plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor-mock'] --- import kbnCodeEditorMockObj from './kbn_code_editor_mock.devdocs.json'; diff --git a/api_docs/kbn_code_owners.mdx b/api_docs/kbn_code_owners.mdx index 77b7e7801ae7c..174423a95d79a 100644 --- a/api_docs/kbn_code_owners.mdx +++ b/api_docs/kbn_code_owners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-owners title: "@kbn/code-owners" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-owners plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-owners'] --- import kbnCodeOwnersObj from './kbn_code_owners.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index 44181a7e1b849..9c4d99c70de80 100644 --- a/api_docs/kbn_coloring.mdx +++ b/api_docs/kbn_coloring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-coloring title: "@kbn/coloring" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/coloring plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/coloring'] --- import kbnColoringObj from './kbn_coloring.devdocs.json'; diff --git a/api_docs/kbn_config.mdx b/api_docs/kbn_config.mdx index 038c4c9ecccb0..2ee605c460cca 100644 --- a/api_docs/kbn_config.mdx +++ b/api_docs/kbn_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config title: "@kbn/config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config'] --- import kbnConfigObj from './kbn_config.devdocs.json'; diff --git a/api_docs/kbn_config_mocks.mdx b/api_docs/kbn_config_mocks.mdx index c301e1a8efeda..53118952a89d2 100644 --- a/api_docs/kbn_config_mocks.mdx +++ b/api_docs/kbn_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-mocks title: "@kbn/config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-mocks'] --- import kbnConfigMocksObj from './kbn_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_config_schema.mdx b/api_docs/kbn_config_schema.mdx index 12f975d3bddd3..5fb91b2d0c141 100644 --- a/api_docs/kbn_config_schema.mdx +++ b/api_docs/kbn_config_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-schema title: "@kbn/config-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-schema plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] --- import kbnConfigSchemaObj from './kbn_config_schema.devdocs.json'; diff --git a/api_docs/kbn_content_management_content_editor.mdx b/api_docs/kbn_content_management_content_editor.mdx index 3942e82a237b2..a9aed94e07e8d 100644 --- a/api_docs/kbn_content_management_content_editor.mdx +++ b/api_docs/kbn_content_management_content_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-editor title: "@kbn/content-management-content-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-editor plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-content-editor'] --- import kbnContentManagementContentEditorObj from './kbn_content_management_content_editor.devdocs.json'; diff --git a/api_docs/kbn_content_management_content_insights_public.mdx b/api_docs/kbn_content_management_content_insights_public.mdx index fe1b9fdd83472..dbfcbb7c4b397 100644 --- a/api_docs/kbn_content_management_content_insights_public.mdx +++ b/api_docs/kbn_content_management_content_insights_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-insights-public title: "@kbn/content-management-content-insights-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-insights-public plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-content-insights-public'] --- import kbnContentManagementContentInsightsPublicObj from './kbn_content_management_content_insights_public.devdocs.json'; diff --git a/api_docs/kbn_content_management_content_insights_server.mdx b/api_docs/kbn_content_management_content_insights_server.mdx index d9f2e90b39d18..8899cc3a4313a 100644 --- a/api_docs/kbn_content_management_content_insights_server.mdx +++ b/api_docs/kbn_content_management_content_insights_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-insights-server title: "@kbn/content-management-content-insights-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-insights-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-content-insights-server'] --- import kbnContentManagementContentInsightsServerObj from './kbn_content_management_content_insights_server.devdocs.json'; diff --git a/api_docs/kbn_content_management_favorites_public.mdx b/api_docs/kbn_content_management_favorites_public.mdx index 6720e63f4dc4f..8341e69175997 100644 --- a/api_docs/kbn_content_management_favorites_public.mdx +++ b/api_docs/kbn_content_management_favorites_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-favorites-public title: "@kbn/content-management-favorites-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-favorites-public plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-favorites-public'] --- import kbnContentManagementFavoritesPublicObj from './kbn_content_management_favorites_public.devdocs.json'; diff --git a/api_docs/kbn_content_management_favorites_server.mdx b/api_docs/kbn_content_management_favorites_server.mdx index f71c4f87de9d4..785da53c60ed8 100644 --- a/api_docs/kbn_content_management_favorites_server.mdx +++ b/api_docs/kbn_content_management_favorites_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-favorites-server title: "@kbn/content-management-favorites-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-favorites-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-favorites-server'] --- import kbnContentManagementFavoritesServerObj from './kbn_content_management_favorites_server.devdocs.json'; diff --git a/api_docs/kbn_content_management_tabbed_table_list_view.mdx b/api_docs/kbn_content_management_tabbed_table_list_view.mdx index 52f25b5983ce0..de028e6598bd6 100644 --- a/api_docs/kbn_content_management_tabbed_table_list_view.mdx +++ b/api_docs/kbn_content_management_tabbed_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-tabbed-table-list-view title: "@kbn/content-management-tabbed-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-tabbed-table-list-view plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-tabbed-table-list-view'] --- import kbnContentManagementTabbedTableListViewObj from './kbn_content_management_tabbed_table_list_view.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view.mdx b/api_docs/kbn_content_management_table_list_view.mdx index bfa1c96ca331b..73b048e08154e 100644 --- a/api_docs/kbn_content_management_table_list_view.mdx +++ b/api_docs/kbn_content_management_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view title: "@kbn/content-management-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view'] --- import kbnContentManagementTableListViewObj from './kbn_content_management_table_list_view.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view_common.mdx b/api_docs/kbn_content_management_table_list_view_common.mdx index e90dc8288d59b..9a3c865e005f8 100644 --- a/api_docs/kbn_content_management_table_list_view_common.mdx +++ b/api_docs/kbn_content_management_table_list_view_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view-common title: "@kbn/content-management-table-list-view-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view-common plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view-common'] --- import kbnContentManagementTableListViewCommonObj from './kbn_content_management_table_list_view_common.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view_table.mdx b/api_docs/kbn_content_management_table_list_view_table.mdx index 6c63caf708a0d..d2cf6ee91a84c 100644 --- a/api_docs/kbn_content_management_table_list_view_table.mdx +++ b/api_docs/kbn_content_management_table_list_view_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view-table title: "@kbn/content-management-table-list-view-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view-table plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view-table'] --- import kbnContentManagementTableListViewTableObj from './kbn_content_management_table_list_view_table.devdocs.json'; diff --git a/api_docs/kbn_content_management_user_profiles.mdx b/api_docs/kbn_content_management_user_profiles.mdx index a9ad063b7c976..af6c3935e8c9b 100644 --- a/api_docs/kbn_content_management_user_profiles.mdx +++ b/api_docs/kbn_content_management_user_profiles.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-user-profiles title: "@kbn/content-management-user-profiles" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-user-profiles plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-user-profiles'] --- import kbnContentManagementUserProfilesObj from './kbn_content_management_user_profiles.devdocs.json'; diff --git a/api_docs/kbn_content_management_utils.mdx b/api_docs/kbn_content_management_utils.mdx index d8d4dff31c00f..59151eb6baadf 100644 --- a/api_docs/kbn_content_management_utils.mdx +++ b/api_docs/kbn_content_management_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-utils title: "@kbn/content-management-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-utils'] --- import kbnContentManagementUtilsObj from './kbn_content_management_utils.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index 924b854d5418a..f25dfb95fab47 100644 --- a/api_docs/kbn_core_analytics_browser.mdx +++ b/api_docs/kbn_core_analytics_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser title: "@kbn/core-analytics-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser'] --- import kbnCoreAnalyticsBrowserObj from './kbn_core_analytics_browser.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_internal.mdx b/api_docs/kbn_core_analytics_browser_internal.mdx index 113c5416c0f41..bbf2d179dcbc5 100644 --- a/api_docs/kbn_core_analytics_browser_internal.mdx +++ b/api_docs/kbn_core_analytics_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-internal title: "@kbn/core-analytics-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-internal'] --- import kbnCoreAnalyticsBrowserInternalObj from './kbn_core_analytics_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_mocks.mdx b/api_docs/kbn_core_analytics_browser_mocks.mdx index 40398f1db238f..3a74985beddf3 100644 --- a/api_docs/kbn_core_analytics_browser_mocks.mdx +++ b/api_docs/kbn_core_analytics_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-mocks title: "@kbn/core-analytics-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-mocks'] --- import kbnCoreAnalyticsBrowserMocksObj from './kbn_core_analytics_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server.mdx b/api_docs/kbn_core_analytics_server.mdx index 284d8cd7d4ccf..964cbf910f03b 100644 --- a/api_docs/kbn_core_analytics_server.mdx +++ b/api_docs/kbn_core_analytics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server title: "@kbn/core-analytics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server'] --- import kbnCoreAnalyticsServerObj from './kbn_core_analytics_server.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_internal.mdx b/api_docs/kbn_core_analytics_server_internal.mdx index f83bfa515475d..a3a6061eb3c60 100644 --- a/api_docs/kbn_core_analytics_server_internal.mdx +++ b/api_docs/kbn_core_analytics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-internal title: "@kbn/core-analytics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-internal'] --- import kbnCoreAnalyticsServerInternalObj from './kbn_core_analytics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_mocks.mdx b/api_docs/kbn_core_analytics_server_mocks.mdx index d01a26fd68271..18ced0b6d02e5 100644 --- a/api_docs/kbn_core_analytics_server_mocks.mdx +++ b/api_docs/kbn_core_analytics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-mocks title: "@kbn/core-analytics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-mocks'] --- import kbnCoreAnalyticsServerMocksObj from './kbn_core_analytics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser.mdx b/api_docs/kbn_core_application_browser.mdx index 3400325b49a5d..5f3a7ee2a8cc4 100644 --- a/api_docs/kbn_core_application_browser.mdx +++ b/api_docs/kbn_core_application_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser title: "@kbn/core-application-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser'] --- import kbnCoreApplicationBrowserObj from './kbn_core_application_browser.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_internal.mdx b/api_docs/kbn_core_application_browser_internal.mdx index 470068332c2ac..a094d806f8599 100644 --- a/api_docs/kbn_core_application_browser_internal.mdx +++ b/api_docs/kbn_core_application_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-internal title: "@kbn/core-application-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-internal'] --- import kbnCoreApplicationBrowserInternalObj from './kbn_core_application_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_mocks.mdx b/api_docs/kbn_core_application_browser_mocks.mdx index 4e2d99fc7cc83..2e58d19b70acb 100644 --- a/api_docs/kbn_core_application_browser_mocks.mdx +++ b/api_docs/kbn_core_application_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-mocks title: "@kbn/core-application-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-mocks'] --- import kbnCoreApplicationBrowserMocksObj from './kbn_core_application_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_common.mdx b/api_docs/kbn_core_application_common.mdx index c1c4073ae01a7..4f740160e82af 100644 --- a/api_docs/kbn_core_application_common.mdx +++ b/api_docs/kbn_core_application_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-common title: "@kbn/core-application-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-common plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-common'] --- import kbnCoreApplicationCommonObj from './kbn_core_application_common.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_internal.mdx b/api_docs/kbn_core_apps_browser_internal.mdx index 6f31fc74b0e56..81f892e1bcff3 100644 --- a/api_docs/kbn_core_apps_browser_internal.mdx +++ b/api_docs/kbn_core_apps_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-internal title: "@kbn/core-apps-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-internal'] --- import kbnCoreAppsBrowserInternalObj from './kbn_core_apps_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_mocks.mdx b/api_docs/kbn_core_apps_browser_mocks.mdx index 2482fc43348fa..4314c27ccf92d 100644 --- a/api_docs/kbn_core_apps_browser_mocks.mdx +++ b/api_docs/kbn_core_apps_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-mocks title: "@kbn/core-apps-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-mocks'] --- import kbnCoreAppsBrowserMocksObj from './kbn_core_apps_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_apps_server_internal.mdx b/api_docs/kbn_core_apps_server_internal.mdx index 8327e2b8a5331..a320e01a2f404 100644 --- a/api_docs/kbn_core_apps_server_internal.mdx +++ b/api_docs/kbn_core_apps_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-server-internal title: "@kbn/core-apps-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-server-internal'] --- import kbnCoreAppsServerInternalObj from './kbn_core_apps_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_browser_mocks.mdx b/api_docs/kbn_core_base_browser_mocks.mdx index da11e2bb1e490..81e7708376b0a 100644 --- a/api_docs/kbn_core_base_browser_mocks.mdx +++ b/api_docs/kbn_core_base_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-browser-mocks title: "@kbn/core-base-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-browser-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-browser-mocks'] --- import kbnCoreBaseBrowserMocksObj from './kbn_core_base_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_base_common.mdx b/api_docs/kbn_core_base_common.mdx index 21608655c9be9..e5098bd997ccb 100644 --- a/api_docs/kbn_core_base_common.mdx +++ b/api_docs/kbn_core_base_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-common title: "@kbn/core-base-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-common plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-common'] --- import kbnCoreBaseCommonObj from './kbn_core_base_common.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_internal.mdx b/api_docs/kbn_core_base_server_internal.mdx index 4571d240ed6cb..2d624c58510e3 100644 --- a/api_docs/kbn_core_base_server_internal.mdx +++ b/api_docs/kbn_core_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-internal title: "@kbn/core-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-internal'] --- import kbnCoreBaseServerInternalObj from './kbn_core_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_mocks.mdx b/api_docs/kbn_core_base_server_mocks.mdx index 0e29194bcc5c6..e2c3f323b26a3 100644 --- a/api_docs/kbn_core_base_server_mocks.mdx +++ b/api_docs/kbn_core_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-mocks title: "@kbn/core-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-mocks'] --- import kbnCoreBaseServerMocksObj from './kbn_core_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_browser_mocks.mdx b/api_docs/kbn_core_capabilities_browser_mocks.mdx index 9b2d6fed31056..531595ebc8193 100644 --- a/api_docs/kbn_core_capabilities_browser_mocks.mdx +++ b/api_docs/kbn_core_capabilities_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-browser-mocks title: "@kbn/core-capabilities-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-browser-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-browser-mocks'] --- import kbnCoreCapabilitiesBrowserMocksObj from './kbn_core_capabilities_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_common.mdx b/api_docs/kbn_core_capabilities_common.mdx index 06d5ebce7ba61..dadf8f470c8af 100644 --- a/api_docs/kbn_core_capabilities_common.mdx +++ b/api_docs/kbn_core_capabilities_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-common title: "@kbn/core-capabilities-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-common plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-common'] --- import kbnCoreCapabilitiesCommonObj from './kbn_core_capabilities_common.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server.mdx b/api_docs/kbn_core_capabilities_server.mdx index b6d5b0ce6f227..02c766cd79f14 100644 --- a/api_docs/kbn_core_capabilities_server.mdx +++ b/api_docs/kbn_core_capabilities_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server title: "@kbn/core-capabilities-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server'] --- import kbnCoreCapabilitiesServerObj from './kbn_core_capabilities_server.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server_mocks.mdx b/api_docs/kbn_core_capabilities_server_mocks.mdx index 6b009783e6311..5f77943d40f84 100644 --- a/api_docs/kbn_core_capabilities_server_mocks.mdx +++ b/api_docs/kbn_core_capabilities_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server-mocks title: "@kbn/core-capabilities-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server-mocks'] --- import kbnCoreCapabilitiesServerMocksObj from './kbn_core_capabilities_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser.devdocs.json b/api_docs/kbn_core_chrome_browser.devdocs.json index f7a208fe6ec45..63ea89a56f6e3 100644 --- a/api_docs/kbn_core_chrome_browser.devdocs.json +++ b/api_docs/kbn_core_chrome_browser.devdocs.json @@ -3923,7 +3923,7 @@ "label": "CloudLinkId", "description": [], "signature": [ - "\"projects\" | \"userAndRoles\" | \"performance\" | \"billingAndSub\" | \"deployment\" | \"deployments\"" + "\"projects\" | \"deployment\" | \"userAndRoles\" | \"performance\" | \"billingAndSub\" | \"deployments\"" ], "path": "packages/core/chrome/core-chrome-browser/src/project_navigation.ts", "deprecated": false, @@ -3946,7 +3946,7 @@ "section": "def-public.CloudLink", "text": "CloudLink" }, - " | undefined; userAndRoles?: ", + " | undefined; deployment?: ", { "pluginId": "@kbn/core-chrome-browser", "scope": "public", @@ -3954,7 +3954,7 @@ "section": "def-public.CloudLink", "text": "CloudLink" }, - " | undefined; performance?: ", + " | undefined; userAndRoles?: ", { "pluginId": "@kbn/core-chrome-browser", "scope": "public", @@ -3962,7 +3962,7 @@ "section": "def-public.CloudLink", "text": "CloudLink" }, - " | undefined; billingAndSub?: ", + " | undefined; performance?: ", { "pluginId": "@kbn/core-chrome-browser", "scope": "public", @@ -3970,7 +3970,7 @@ "section": "def-public.CloudLink", "text": "CloudLink" }, - " | undefined; deployment?: ", + " | undefined; billingAndSub?: ", { "pluginId": "@kbn/core-chrome-browser", "scope": "public", diff --git a/api_docs/kbn_core_chrome_browser.mdx b/api_docs/kbn_core_chrome_browser.mdx index 7b65b3cb4cf35..c02da34256138 100644 --- a/api_docs/kbn_core_chrome_browser.mdx +++ b/api_docs/kbn_core_chrome_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser title: "@kbn/core-chrome-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser'] --- import kbnCoreChromeBrowserObj from './kbn_core_chrome_browser.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser_mocks.mdx b/api_docs/kbn_core_chrome_browser_mocks.mdx index 434c2e86dea34..ff39e7962dbad 100644 --- a/api_docs/kbn_core_chrome_browser_mocks.mdx +++ b/api_docs/kbn_core_chrome_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser-mocks title: "@kbn/core-chrome-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser-mocks'] --- import kbnCoreChromeBrowserMocksObj from './kbn_core_chrome_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_config_server_internal.mdx b/api_docs/kbn_core_config_server_internal.mdx index f1de762803109..1df8dfa854c9a 100644 --- a/api_docs/kbn_core_config_server_internal.mdx +++ b/api_docs/kbn_core_config_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-config-server-internal title: "@kbn/core-config-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-config-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-config-server-internal'] --- import kbnCoreConfigServerInternalObj from './kbn_core_config_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser.mdx b/api_docs/kbn_core_custom_branding_browser.mdx index a5c1a6edf0e17..fd2baee1848ee 100644 --- a/api_docs/kbn_core_custom_branding_browser.mdx +++ b/api_docs/kbn_core_custom_branding_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser title: "@kbn/core-custom-branding-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser'] --- import kbnCoreCustomBrandingBrowserObj from './kbn_core_custom_branding_browser.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_internal.mdx b/api_docs/kbn_core_custom_branding_browser_internal.mdx index f5f1777db6f19..2e2e8f78dab89 100644 --- a/api_docs/kbn_core_custom_branding_browser_internal.mdx +++ b/api_docs/kbn_core_custom_branding_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-internal title: "@kbn/core-custom-branding-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-internal'] --- import kbnCoreCustomBrandingBrowserInternalObj from './kbn_core_custom_branding_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_mocks.mdx b/api_docs/kbn_core_custom_branding_browser_mocks.mdx index 25ca888fc4779..c56ddefc9dec7 100644 --- a/api_docs/kbn_core_custom_branding_browser_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-mocks title: "@kbn/core-custom-branding-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-mocks'] --- import kbnCoreCustomBrandingBrowserMocksObj from './kbn_core_custom_branding_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_common.mdx b/api_docs/kbn_core_custom_branding_common.mdx index 3d4dcbe6803ec..079cf42867a04 100644 --- a/api_docs/kbn_core_custom_branding_common.mdx +++ b/api_docs/kbn_core_custom_branding_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-common title: "@kbn/core-custom-branding-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-common plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-common'] --- import kbnCoreCustomBrandingCommonObj from './kbn_core_custom_branding_common.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server.mdx b/api_docs/kbn_core_custom_branding_server.mdx index 1cdb86293f4a5..3ef3462c99098 100644 --- a/api_docs/kbn_core_custom_branding_server.mdx +++ b/api_docs/kbn_core_custom_branding_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server title: "@kbn/core-custom-branding-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server'] --- import kbnCoreCustomBrandingServerObj from './kbn_core_custom_branding_server.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server_internal.mdx b/api_docs/kbn_core_custom_branding_server_internal.mdx index 32433f7277d4a..e5c456d770914 100644 --- a/api_docs/kbn_core_custom_branding_server_internal.mdx +++ b/api_docs/kbn_core_custom_branding_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-internal title: "@kbn/core-custom-branding-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-internal'] --- import kbnCoreCustomBrandingServerInternalObj from './kbn_core_custom_branding_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server_mocks.mdx b/api_docs/kbn_core_custom_branding_server_mocks.mdx index e9601f490b8ed..03ec12a9d8c1e 100644 --- a/api_docs/kbn_core_custom_branding_server_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-mocks title: "@kbn/core-custom-branding-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-mocks'] --- import kbnCoreCustomBrandingServerMocksObj from './kbn_core_custom_branding_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser.mdx b/api_docs/kbn_core_deprecations_browser.mdx index cb0effdc979d8..cb5d44fbdcef6 100644 --- a/api_docs/kbn_core_deprecations_browser.mdx +++ b/api_docs/kbn_core_deprecations_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser title: "@kbn/core-deprecations-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser'] --- import kbnCoreDeprecationsBrowserObj from './kbn_core_deprecations_browser.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_internal.mdx b/api_docs/kbn_core_deprecations_browser_internal.mdx index 9116cf1a14288..751bba4f52f98 100644 --- a/api_docs/kbn_core_deprecations_browser_internal.mdx +++ b/api_docs/kbn_core_deprecations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-internal title: "@kbn/core-deprecations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-internal'] --- import kbnCoreDeprecationsBrowserInternalObj from './kbn_core_deprecations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_mocks.mdx b/api_docs/kbn_core_deprecations_browser_mocks.mdx index 30515c11e4888..52c094d4dd043 100644 --- a/api_docs/kbn_core_deprecations_browser_mocks.mdx +++ b/api_docs/kbn_core_deprecations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-mocks title: "@kbn/core-deprecations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-mocks'] --- import kbnCoreDeprecationsBrowserMocksObj from './kbn_core_deprecations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_common.devdocs.json b/api_docs/kbn_core_deprecations_common.devdocs.json index d1055cac144e9..59c42fc7e5ed2 100644 --- a/api_docs/kbn_core_deprecations_common.devdocs.json +++ b/api_docs/kbn_core_deprecations_common.devdocs.json @@ -49,12 +49,15 @@ { "parentPluginId": "@kbn/core-deprecations-common", "id": "def-common.BaseDeprecationDetails.message", - "type": "string", + "type": "CompoundType", "tags": [], "label": "message", "description": [ "\nThe description message to be displayed for the deprecation.\nCheck the README for writing deprecations in `src/core/server/deprecations/README.mdx`" ], + "signature": [ + "string | string[]" + ], "path": "packages/core/deprecations/core-deprecations-common/src/types.ts", "deprecated": false, "trackAdoption": false @@ -85,7 +88,7 @@ "\n(optional) Used to identify between different deprecation types.\nExample use case: in Upgrade Assistant, we may want to allow the user to sort by\ndeprecation type or show each type in a separate tab.\n\nFeel free to add new types if necessary.\nPredefined types are necessary to reduce having similar definitions with different keywords\nacross kibana deprecations." ], "signature": [ - "\"config\" | \"feature\" | undefined" + "\"config\" | \"api\" | \"feature\" | undefined" ], "path": "packages/core/deprecations/core-deprecations-common/src/types.ts", "deprecated": false, @@ -133,7 +136,7 @@ "corrective action needed to fix this deprecation." ], "signature": [ - "{ api?: { path: string; method: \"POST\" | \"PUT\"; body?: { [key: string]: any; } | undefined; omitContextFromBody?: boolean | undefined; } | undefined; manualSteps: string[]; }" + "{ api?: { path: string; method: \"POST\" | \"PUT\"; body?: { [key: string]: any; } | undefined; omitContextFromBody?: boolean | undefined; } | undefined; manualSteps: string[]; mark_as_resolved_api?: { apiTotalCalls: number; totalMarkedAsResolved: number; timestamp: string | number | Date; routePath: string; routeMethod: string; routeVersion?: string | undefined; } | undefined; }" ], "path": "packages/core/deprecations/core-deprecations-common/src/types.ts", "deprecated": false, @@ -297,6 +300,8 @@ "text": "ConfigDeprecationDetails" }, " | ", + "ApiDeprecationDetails", + " | ", { "pluginId": "@kbn/core-deprecations-common", "scope": "common", diff --git a/api_docs/kbn_core_deprecations_common.mdx b/api_docs/kbn_core_deprecations_common.mdx index 76d1064e1ce7f..6a4fa95fe88b3 100644 --- a/api_docs/kbn_core_deprecations_common.mdx +++ b/api_docs/kbn_core_deprecations_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-common title: "@kbn/core-deprecations-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-common plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-common'] --- import kbnCoreDeprecationsCommonObj from './kbn_core_deprecations_common.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 17 | 0 | 9 | 0 | +| 17 | 0 | 9 | 1 | ## Common diff --git a/api_docs/kbn_core_deprecations_server.mdx b/api_docs/kbn_core_deprecations_server.mdx index e886a575c39b3..fc4ccdfdddb83 100644 --- a/api_docs/kbn_core_deprecations_server.mdx +++ b/api_docs/kbn_core_deprecations_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server title: "@kbn/core-deprecations-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server'] --- import kbnCoreDeprecationsServerObj from './kbn_core_deprecations_server.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_internal.mdx b/api_docs/kbn_core_deprecations_server_internal.mdx index b2a0f67f9ea60..21518ac9a29e0 100644 --- a/api_docs/kbn_core_deprecations_server_internal.mdx +++ b/api_docs/kbn_core_deprecations_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-internal title: "@kbn/core-deprecations-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-internal'] --- import kbnCoreDeprecationsServerInternalObj from './kbn_core_deprecations_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_mocks.mdx b/api_docs/kbn_core_deprecations_server_mocks.mdx index fc769f3762bdd..1a1355df10361 100644 --- a/api_docs/kbn_core_deprecations_server_mocks.mdx +++ b/api_docs/kbn_core_deprecations_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-mocks title: "@kbn/core-deprecations-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-mocks'] --- import kbnCoreDeprecationsServerMocksObj from './kbn_core_deprecations_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser.mdx b/api_docs/kbn_core_doc_links_browser.mdx index e6ceebf56b9f3..97605f15b827f 100644 --- a/api_docs/kbn_core_doc_links_browser.mdx +++ b/api_docs/kbn_core_doc_links_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser title: "@kbn/core-doc-links-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser'] --- import kbnCoreDocLinksBrowserObj from './kbn_core_doc_links_browser.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser_mocks.mdx b/api_docs/kbn_core_doc_links_browser_mocks.mdx index b98a711783ece..26d575ee33126 100644 --- a/api_docs/kbn_core_doc_links_browser_mocks.mdx +++ b/api_docs/kbn_core_doc_links_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser-mocks title: "@kbn/core-doc-links-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser-mocks'] --- import kbnCoreDocLinksBrowserMocksObj from './kbn_core_doc_links_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server.mdx b/api_docs/kbn_core_doc_links_server.mdx index ed22aaaa96c61..3ffbb6cdb879b 100644 --- a/api_docs/kbn_core_doc_links_server.mdx +++ b/api_docs/kbn_core_doc_links_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server title: "@kbn/core-doc-links-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server'] --- import kbnCoreDocLinksServerObj from './kbn_core_doc_links_server.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server_mocks.mdx b/api_docs/kbn_core_doc_links_server_mocks.mdx index f4b4767486a18..c638293a2c85e 100644 --- a/api_docs/kbn_core_doc_links_server_mocks.mdx +++ b/api_docs/kbn_core_doc_links_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server-mocks title: "@kbn/core-doc-links-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server-mocks'] --- import kbnCoreDocLinksServerMocksObj from './kbn_core_doc_links_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx index 728c729ea2840..57dfbfdc906bb 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-internal title: "@kbn/core-elasticsearch-client-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-internal'] --- import kbnCoreElasticsearchClientServerInternalObj from './kbn_core_elasticsearch_client_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx index 23d9813cb40f5..405b267f514f5 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-mocks title: "@kbn/core-elasticsearch-client-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-mocks'] --- import kbnCoreElasticsearchClientServerMocksObj from './kbn_core_elasticsearch_client_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server.mdx b/api_docs/kbn_core_elasticsearch_server.mdx index 87e428ec9f4f9..64ac94db7fb8a 100644 --- a/api_docs/kbn_core_elasticsearch_server.mdx +++ b/api_docs/kbn_core_elasticsearch_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server title: "@kbn/core-elasticsearch-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server'] --- import kbnCoreElasticsearchServerObj from './kbn_core_elasticsearch_server.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_internal.mdx b/api_docs/kbn_core_elasticsearch_server_internal.mdx index f124f4f9efc04..061c059daa9c8 100644 --- a/api_docs/kbn_core_elasticsearch_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-internal title: "@kbn/core-elasticsearch-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-internal'] --- import kbnCoreElasticsearchServerInternalObj from './kbn_core_elasticsearch_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_server_mocks.mdx index e3746ce9b495f..e7199cf6ffe7a 100644 --- a/api_docs/kbn_core_elasticsearch_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-mocks title: "@kbn/core-elasticsearch-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-mocks'] --- import kbnCoreElasticsearchServerMocksObj from './kbn_core_elasticsearch_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_internal.mdx b/api_docs/kbn_core_environment_server_internal.mdx index 1568c94eaff9c..6df6690e3457b 100644 --- a/api_docs/kbn_core_environment_server_internal.mdx +++ b/api_docs/kbn_core_environment_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-internal title: "@kbn/core-environment-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-internal'] --- import kbnCoreEnvironmentServerInternalObj from './kbn_core_environment_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_mocks.mdx b/api_docs/kbn_core_environment_server_mocks.mdx index cdbc07f38dd2b..28fa9b86a7578 100644 --- a/api_docs/kbn_core_environment_server_mocks.mdx +++ b/api_docs/kbn_core_environment_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-mocks title: "@kbn/core-environment-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-mocks'] --- import kbnCoreEnvironmentServerMocksObj from './kbn_core_environment_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser.mdx b/api_docs/kbn_core_execution_context_browser.mdx index 2a9f223a40cda..e341059491bd7 100644 --- a/api_docs/kbn_core_execution_context_browser.mdx +++ b/api_docs/kbn_core_execution_context_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser title: "@kbn/core-execution-context-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser'] --- import kbnCoreExecutionContextBrowserObj from './kbn_core_execution_context_browser.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_internal.mdx b/api_docs/kbn_core_execution_context_browser_internal.mdx index f56d3255ac596..054a5692f885b 100644 --- a/api_docs/kbn_core_execution_context_browser_internal.mdx +++ b/api_docs/kbn_core_execution_context_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-internal title: "@kbn/core-execution-context-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-internal'] --- import kbnCoreExecutionContextBrowserInternalObj from './kbn_core_execution_context_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_mocks.mdx b/api_docs/kbn_core_execution_context_browser_mocks.mdx index 78c07c0a6cd74..54b6932ecbe05 100644 --- a/api_docs/kbn_core_execution_context_browser_mocks.mdx +++ b/api_docs/kbn_core_execution_context_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-mocks title: "@kbn/core-execution-context-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-mocks'] --- import kbnCoreExecutionContextBrowserMocksObj from './kbn_core_execution_context_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_common.mdx b/api_docs/kbn_core_execution_context_common.mdx index 81b3a5805d9a3..dbe12fc157179 100644 --- a/api_docs/kbn_core_execution_context_common.mdx +++ b/api_docs/kbn_core_execution_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-common title: "@kbn/core-execution-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-common plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-common'] --- import kbnCoreExecutionContextCommonObj from './kbn_core_execution_context_common.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server.mdx b/api_docs/kbn_core_execution_context_server.mdx index 062026c011049..67b7adfa617a1 100644 --- a/api_docs/kbn_core_execution_context_server.mdx +++ b/api_docs/kbn_core_execution_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server title: "@kbn/core-execution-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server'] --- import kbnCoreExecutionContextServerObj from './kbn_core_execution_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_internal.mdx b/api_docs/kbn_core_execution_context_server_internal.mdx index 18b74907bd0e9..2aa14b65ccb95 100644 --- a/api_docs/kbn_core_execution_context_server_internal.mdx +++ b/api_docs/kbn_core_execution_context_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-internal title: "@kbn/core-execution-context-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-internal'] --- import kbnCoreExecutionContextServerInternalObj from './kbn_core_execution_context_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_mocks.mdx b/api_docs/kbn_core_execution_context_server_mocks.mdx index e882699d48977..0b278913b9db7 100644 --- a/api_docs/kbn_core_execution_context_server_mocks.mdx +++ b/api_docs/kbn_core_execution_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-mocks title: "@kbn/core-execution-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-mocks'] --- import kbnCoreExecutionContextServerMocksObj from './kbn_core_execution_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser.mdx b/api_docs/kbn_core_fatal_errors_browser.mdx index 7b3c4ae70bcde..b65d59e89ca27 100644 --- a/api_docs/kbn_core_fatal_errors_browser.mdx +++ b/api_docs/kbn_core_fatal_errors_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser title: "@kbn/core-fatal-errors-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser'] --- import kbnCoreFatalErrorsBrowserObj from './kbn_core_fatal_errors_browser.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx index cac504944fc74..e66072b375616 100644 --- a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx +++ b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser-mocks title: "@kbn/core-fatal-errors-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser-mocks'] --- import kbnCoreFatalErrorsBrowserMocksObj from './kbn_core_fatal_errors_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_feature_flags_browser.mdx b/api_docs/kbn_core_feature_flags_browser.mdx index d04a20182f3a8..3e2ed93e38f83 100644 --- a/api_docs/kbn_core_feature_flags_browser.mdx +++ b/api_docs/kbn_core_feature_flags_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-feature-flags-browser title: "@kbn/core-feature-flags-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-feature-flags-browser plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-feature-flags-browser'] --- import kbnCoreFeatureFlagsBrowserObj from './kbn_core_feature_flags_browser.devdocs.json'; diff --git a/api_docs/kbn_core_feature_flags_browser_internal.mdx b/api_docs/kbn_core_feature_flags_browser_internal.mdx index 8000388b45be9..a146bc963ad9a 100644 --- a/api_docs/kbn_core_feature_flags_browser_internal.mdx +++ b/api_docs/kbn_core_feature_flags_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-feature-flags-browser-internal title: "@kbn/core-feature-flags-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-feature-flags-browser-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-feature-flags-browser-internal'] --- import kbnCoreFeatureFlagsBrowserInternalObj from './kbn_core_feature_flags_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_feature_flags_browser_mocks.mdx b/api_docs/kbn_core_feature_flags_browser_mocks.mdx index 26ce39d371a5f..a7c37e6038174 100644 --- a/api_docs/kbn_core_feature_flags_browser_mocks.mdx +++ b/api_docs/kbn_core_feature_flags_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-feature-flags-browser-mocks title: "@kbn/core-feature-flags-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-feature-flags-browser-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-feature-flags-browser-mocks'] --- import kbnCoreFeatureFlagsBrowserMocksObj from './kbn_core_feature_flags_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_feature_flags_server.mdx b/api_docs/kbn_core_feature_flags_server.mdx index 73e37666306b7..337fe2d9570f0 100644 --- a/api_docs/kbn_core_feature_flags_server.mdx +++ b/api_docs/kbn_core_feature_flags_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-feature-flags-server title: "@kbn/core-feature-flags-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-feature-flags-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-feature-flags-server'] --- import kbnCoreFeatureFlagsServerObj from './kbn_core_feature_flags_server.devdocs.json'; diff --git a/api_docs/kbn_core_feature_flags_server_internal.mdx b/api_docs/kbn_core_feature_flags_server_internal.mdx index a7bf94bb7a882..405a96e6dbca1 100644 --- a/api_docs/kbn_core_feature_flags_server_internal.mdx +++ b/api_docs/kbn_core_feature_flags_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-feature-flags-server-internal title: "@kbn/core-feature-flags-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-feature-flags-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-feature-flags-server-internal'] --- import kbnCoreFeatureFlagsServerInternalObj from './kbn_core_feature_flags_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_feature_flags_server_mocks.mdx b/api_docs/kbn_core_feature_flags_server_mocks.mdx index 1996eb463c1d7..96c6686bc3743 100644 --- a/api_docs/kbn_core_feature_flags_server_mocks.mdx +++ b/api_docs/kbn_core_feature_flags_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-feature-flags-server-mocks title: "@kbn/core-feature-flags-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-feature-flags-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-feature-flags-server-mocks'] --- import kbnCoreFeatureFlagsServerMocksObj from './kbn_core_feature_flags_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser.mdx b/api_docs/kbn_core_http_browser.mdx index a964e5377f7c7..01b6833115b1b 100644 --- a/api_docs/kbn_core_http_browser.mdx +++ b/api_docs/kbn_core_http_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser title: "@kbn/core-http-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser'] --- import kbnCoreHttpBrowserObj from './kbn_core_http_browser.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_internal.mdx b/api_docs/kbn_core_http_browser_internal.mdx index 71c79a4346dca..b0f4942993d4b 100644 --- a/api_docs/kbn_core_http_browser_internal.mdx +++ b/api_docs/kbn_core_http_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-internal title: "@kbn/core-http-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-internal'] --- import kbnCoreHttpBrowserInternalObj from './kbn_core_http_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_mocks.mdx b/api_docs/kbn_core_http_browser_mocks.mdx index 7829437ed0584..59eaae56f20d9 100644 --- a/api_docs/kbn_core_http_browser_mocks.mdx +++ b/api_docs/kbn_core_http_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-mocks title: "@kbn/core-http-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-mocks'] --- import kbnCoreHttpBrowserMocksObj from './kbn_core_http_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_common.mdx b/api_docs/kbn_core_http_common.mdx index 9ccdceca0ad94..b614e22a858a6 100644 --- a/api_docs/kbn_core_http_common.mdx +++ b/api_docs/kbn_core_http_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-common title: "@kbn/core-http-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-common plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-common'] --- import kbnCoreHttpCommonObj from './kbn_core_http_common.devdocs.json'; diff --git a/api_docs/kbn_core_http_context_server_mocks.mdx b/api_docs/kbn_core_http_context_server_mocks.mdx index 6b7d2b537f554..7111664eff044 100644 --- a/api_docs/kbn_core_http_context_server_mocks.mdx +++ b/api_docs/kbn_core_http_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-context-server-mocks title: "@kbn/core-http-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-context-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-context-server-mocks'] --- import kbnCoreHttpContextServerMocksObj from './kbn_core_http_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_request_handler_context_server.mdx b/api_docs/kbn_core_http_request_handler_context_server.mdx index afd882d48fb50..3ae73f70441ce 100644 --- a/api_docs/kbn_core_http_request_handler_context_server.mdx +++ b/api_docs/kbn_core_http_request_handler_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-request-handler-context-server title: "@kbn/core-http-request-handler-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-request-handler-context-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-request-handler-context-server'] --- import kbnCoreHttpRequestHandlerContextServerObj from './kbn_core_http_request_handler_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server.mdx b/api_docs/kbn_core_http_resources_server.mdx index d96a4f20e4f8f..6ff011b85f409 100644 --- a/api_docs/kbn_core_http_resources_server.mdx +++ b/api_docs/kbn_core_http_resources_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server title: "@kbn/core-http-resources-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server'] --- import kbnCoreHttpResourcesServerObj from './kbn_core_http_resources_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_internal.mdx b/api_docs/kbn_core_http_resources_server_internal.mdx index 929339d93e018..48b617a3e4163 100644 --- a/api_docs/kbn_core_http_resources_server_internal.mdx +++ b/api_docs/kbn_core_http_resources_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-internal title: "@kbn/core-http-resources-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-internal'] --- import kbnCoreHttpResourcesServerInternalObj from './kbn_core_http_resources_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_mocks.mdx b/api_docs/kbn_core_http_resources_server_mocks.mdx index cdf20c4979ee6..95533bbcded32 100644 --- a/api_docs/kbn_core_http_resources_server_mocks.mdx +++ b/api_docs/kbn_core_http_resources_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-mocks title: "@kbn/core-http-resources-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-mocks'] --- import kbnCoreHttpResourcesServerMocksObj from './kbn_core_http_resources_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_internal.devdocs.json b/api_docs/kbn_core_http_router_server_internal.devdocs.json index 640117913948d..219635cec955e 100644 --- a/api_docs/kbn_core_http_router_server_internal.devdocs.json +++ b/api_docs/kbn_core_http_router_server_internal.devdocs.json @@ -179,7 +179,7 @@ }, "<", "Method", - ">, \"security\" | \"description\" | \"deprecated\" | \"access\" | \"discontinued\"> | undefined; access: ", + ">, \"security\" | \"description\" | \"summary\" | \"deprecated\" | \"access\" | \"discontinued\"> | undefined; access: ", { "pluginId": "@kbn/core-http-server", "scope": "server", @@ -195,7 +195,7 @@ "section": "def-server.RouteSecurity", "text": "RouteSecurity" }, - " | undefined; enableQueryVersion?: boolean | undefined; summary?: string | undefined; description?: string | undefined; deprecated?: boolean | undefined; discontinued?: string | undefined; }" + " | undefined; enableQueryVersion?: boolean | undefined; summary?: string | undefined; description?: string | undefined; discontinued?: string | undefined; }" ], "path": "packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.ts", "deprecated": false, @@ -266,7 +266,7 @@ }, "<", "Method", - ">, \"security\" | \"description\" | \"deprecated\" | \"access\" | \"discontinued\"> | undefined; access: ", + ">, \"security\" | \"description\" | \"summary\" | \"deprecated\" | \"access\" | \"discontinued\"> | undefined; access: ", { "pluginId": "@kbn/core-http-server", "scope": "server", @@ -282,7 +282,7 @@ "section": "def-server.RouteSecurity", "text": "RouteSecurity" }, - " | undefined; enableQueryVersion?: boolean | undefined; summary?: string | undefined; description?: string | undefined; deprecated?: boolean | undefined; discontinued?: string | undefined; }" + " | undefined; enableQueryVersion?: boolean | undefined; summary?: string | undefined; description?: string | undefined; discontinued?: string | undefined; }" ], "path": "packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.ts", "deprecated": false, @@ -353,7 +353,7 @@ }, "<", "Method", - ">, \"security\" | \"description\" | \"deprecated\" | \"access\" | \"discontinued\"> | undefined; access: ", + ">, \"security\" | \"description\" | \"summary\" | \"deprecated\" | \"access\" | \"discontinued\"> | undefined; access: ", { "pluginId": "@kbn/core-http-server", "scope": "server", @@ -369,7 +369,7 @@ "section": "def-server.RouteSecurity", "text": "RouteSecurity" }, - " | undefined; enableQueryVersion?: boolean | undefined; summary?: string | undefined; description?: string | undefined; deprecated?: boolean | undefined; discontinued?: string | undefined; }" + " | undefined; enableQueryVersion?: boolean | undefined; summary?: string | undefined; description?: string | undefined; discontinued?: string | undefined; }" ], "path": "packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.ts", "deprecated": false, @@ -440,7 +440,7 @@ }, "<", "Method", - ">, \"security\" | \"description\" | \"deprecated\" | \"access\" | \"discontinued\"> | undefined; access: ", + ">, \"security\" | \"description\" | \"summary\" | \"deprecated\" | \"access\" | \"discontinued\"> | undefined; access: ", { "pluginId": "@kbn/core-http-server", "scope": "server", @@ -456,7 +456,7 @@ "section": "def-server.RouteSecurity", "text": "RouteSecurity" }, - " | undefined; enableQueryVersion?: boolean | undefined; summary?: string | undefined; description?: string | undefined; deprecated?: boolean | undefined; discontinued?: string | undefined; }" + " | undefined; enableQueryVersion?: boolean | undefined; summary?: string | undefined; description?: string | undefined; discontinued?: string | undefined; }" ], "path": "packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.ts", "deprecated": false, @@ -527,7 +527,7 @@ }, "<", "Method", - ">, \"security\" | \"description\" | \"deprecated\" | \"access\" | \"discontinued\"> | undefined; access: ", + ">, \"security\" | \"description\" | \"summary\" | \"deprecated\" | \"access\" | \"discontinued\"> | undefined; access: ", { "pluginId": "@kbn/core-http-server", "scope": "server", @@ -543,7 +543,7 @@ "section": "def-server.RouteSecurity", "text": "RouteSecurity" }, - " | undefined; enableQueryVersion?: boolean | undefined; summary?: string | undefined; description?: string | undefined; deprecated?: boolean | undefined; discontinued?: string | undefined; }" + " | undefined; enableQueryVersion?: boolean | undefined; summary?: string | undefined; description?: string | undefined; discontinued?: string | undefined; }" ], "path": "packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.ts", "deprecated": false, @@ -560,8 +560,14 @@ "description": [], "signature": [ "() => ", - "VersionedRouterRoute", - "[]" + { + "pluginId": "@kbn/core-http-server", + "scope": "server", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-server.VersionedRouterRoute", + "text": "VersionedRouterRoute" + }, + "[]" ], "path": "packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.ts", "deprecated": false, diff --git a/api_docs/kbn_core_http_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index 8d7bb66ca2575..b3673acdf490f 100644 --- a/api_docs/kbn_core_http_router_server_internal.mdx +++ b/api_docs/kbn_core_http_router_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-internal title: "@kbn/core-http-router-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-internal'] --- import kbnCoreHttpRouterServerInternalObj from './kbn_core_http_router_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_mocks.devdocs.json b/api_docs/kbn_core_http_router_server_mocks.devdocs.json index 24e21bf81d9dc..df244e8c6f8c7 100644 --- a/api_docs/kbn_core_http_router_server_mocks.devdocs.json +++ b/api_docs/kbn_core_http_router_server_mocks.devdocs.json @@ -72,7 +72,7 @@ "section": "def-server.RouteConfigOptions", "text": "RouteConfigOptions" }, - ", \"security\" | \"description\" | \"deprecated\" | \"access\" | \"discontinued\"> | undefined; access: ", + ", \"security\" | \"description\" | \"summary\" | \"deprecated\" | \"access\" | \"discontinued\"> | undefined; access: ", { "pluginId": "@kbn/core-http-server", "scope": "server", @@ -88,7 +88,7 @@ "section": "def-server.RouteSecurity", "text": "RouteSecurity" }, - " | undefined; enableQueryVersion?: boolean | undefined; summary?: string | undefined; description?: string | undefined; deprecated?: boolean | undefined; discontinued?: string | undefined; }" + " | undefined; enableQueryVersion?: boolean | undefined; summary?: string | undefined; description?: string | undefined; discontinued?: string | undefined; }" ], "path": "packages/core/http/core-http-router-server-mocks/src/versioned_router.mock.ts", "deprecated": false, @@ -337,15 +337,15 @@ "section": "def-server.VersionedRouteConfig", "text": "VersionedRouteConfig" }, - "<\"delete\">], unknown>; } & ", + "<\"delete\">], unknown>; getRoutes: jest.MockInstance<", { "pluginId": "@kbn/core-http-server", "scope": "server", "docId": "kibKbnCoreHttpServerPluginApi", - "section": "def-server.VersionedRouter", - "text": "VersionedRouter" + "section": "def-server.VersionedRouterRoute", + "text": "VersionedRouterRoute" }, - " & { getRoute: (method: keyof ", + "[], [], unknown>; } & ", { "pluginId": "@kbn/core-http-server", "scope": "server", @@ -353,15 +353,7 @@ "section": "def-server.VersionedRouter", "text": "VersionedRouter" }, - "<", - { - "pluginId": "@kbn/core-http-server", - "scope": "server", - "docId": "kibKbnCoreHttpServerPluginApi", - "section": "def-server.RequestHandlerContextBase", - "text": "RequestHandlerContextBase" - }, - ">, path: string) => ", + " & { getRoute: (method: \"get\" | \"delete\" | \"post\" | \"put\" | \"patch\", path: string) => ", { "pluginId": "@kbn/core-http-router-server-mocks", "scope": "server", @@ -576,7 +568,7 @@ "section": "def-server.RouterRoute", "text": "RouterRoute" }, - "[], [], unknown>; versioned: ", + "[], [options?: { excludeVersionedRoutes?: boolean | undefined; } | undefined], unknown>; versioned: ", { "pluginId": "@kbn/core-http-server", "scope": "server", diff --git a/api_docs/kbn_core_http_router_server_mocks.mdx b/api_docs/kbn_core_http_router_server_mocks.mdx index c9ef80c6d4acd..bfffd1aca4b13 100644 --- a/api_docs/kbn_core_http_router_server_mocks.mdx +++ b/api_docs/kbn_core_http_router_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-mocks title: "@kbn/core-http-router-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-mocks'] --- import kbnCoreHttpRouterServerMocksObj from './kbn_core_http_router_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_server.devdocs.json b/api_docs/kbn_core_http_server.devdocs.json index 98beaec1e9118..2b48acb81ee21 100644 --- a/api_docs/kbn_core_http_server.devdocs.json +++ b/api_docs/kbn_core_http_server.devdocs.json @@ -400,6 +400,22 @@ "path": "packages/core/http/core-http-server/src/versioning/types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-server.AddVersionOpts.options", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "{ deprecated?: ", + "RouteDeprecationInfo", + " | undefined; } | undefined" + ], + "path": "packages/core/http/core-http-server/src/versioning/types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -2310,6 +2326,32 @@ "trackAdoption": false, "children": [], "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-server.HttpServiceSetup.getDeprecatedRoutes", + "type": "Function", + "tags": [], + "label": "getDeprecatedRoutes", + "description": [ + "\nProvides a list of all registered deprecated routes {{@link RouterDeprecatedRouteDetails | information}}.\nThe routers will be evaluated everytime this function gets called to\naccommodate for any late route registrations" + ], + "signature": [ + "() => ", + { + "pluginId": "@kbn/core-http-server", + "scope": "server", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-server.RouterDeprecatedRouteDetails", + "text": "RouterDeprecatedRouteDetails" + }, + "[]" + ], + "path": "packages/core/http/core-http-server/src/http_contract.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] } ], "initialIsOpen": false @@ -6359,6 +6401,10 @@ "plugin": "@kbn/core-ui-settings-server-internal", "path": "packages/core/ui-settings/core-ui-settings-server-internal/src/routes/internal/validate.ts" }, + { + "plugin": "@kbn/core-deprecations-server-internal", + "path": "packages/core/deprecations/core-deprecations-server-internal/src/routes/resolve_deprecated_api.ts" + }, { "plugin": "@kbn/core-capabilities-server-internal", "path": "packages/core/capabilities/core-capabilities-server-internal/src/routes/resolve_capabilities.ts" @@ -11253,7 +11299,7 @@ "section": "def-server.KibanaRequestRouteOptions", "text": "KibanaRequestRouteOptions" }, - ">; }" + ">; readonly routePath?: string | undefined; }" ], "path": "packages/core/http/core-http-server/src/router/request.ts", "deprecated": false, @@ -11444,6 +11490,22 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-server.KibanaRequest.apiVersion", + "type": "string", + "tags": [], + "label": "apiVersion", + "description": [ + "\nThe versioned route API version of this request." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/http/core-http-server/src/router/request.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "@kbn/core-http-server", "id": "def-server.KibanaRequest.params", @@ -11651,6 +11713,20 @@ "path": "packages/core/http/core-http-server/src/router/request.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-server.KibanaRequestRoute.routePath", + "type": "string", + "tags": [], + "label": "routePath", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/http/core-http-server/src/router/request.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -11783,6 +11859,21 @@ "deprecated": false, "trackAdoption": false, "children": [ + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-server.KibanaRouteOptions.deprecated", + "type": "Object", + "tags": [], + "label": "deprecated", + "description": [], + "signature": [ + "RouteDeprecationInfo", + " | undefined" + ], + "path": "packages/core/http/core-http-server/src/router/request.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "@kbn/core-http-server", "id": "def-server.KibanaRouteOptions.xsrfRequired", @@ -13323,14 +13414,15 @@ { "parentPluginId": "@kbn/core-http-server", "id": "def-server.RouteConfigOptions.deprecated", - "type": "CompoundType", + "type": "Object", "tags": [], "label": "deprecated", "description": [ - "\nSetting this to `true` declares this route to be deprecated. Consumers SHOULD\nrefrain from usage of this route.\n" + "\nDescription of deprecations for this HTTP API.\n" ], "signature": [ - "boolean | undefined" + "RouteDeprecationInfo", + " | undefined" ], "path": "packages/core/http/core-http-server/src/router/route.ts", "deprecated": false, @@ -13412,6 +13504,22 @@ "path": "packages/core/http/core-http-server/src/router/route.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-server.RouteConfigOptions.availability", + "type": "Object", + "tags": [], + "label": "availability", + "description": [ + "\nBased on the the ES API specification (see https://github.com/elastic/elasticsearch-specification)\nKibana APIs can also specify some metadata about API availability.\n\nThis setting is only applicable if your route `access` is `public`.\n" + ], + "signature": [ + "{ stability?: \"experimental\" | \"beta\" | \"stable\" | undefined; since?: string | undefined; } | undefined" + ], + "path": "packages/core/http/core-http-server/src/router/route.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -13520,6 +13628,87 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-server.RouterDeprecatedRouteDetails", + "type": "Interface", + "tags": [], + "label": "RouterDeprecatedRouteDetails", + "description": [], + "path": "packages/core/http/core-http-server/src/router/router.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-server.RouterDeprecatedRouteDetails.routeDeprecationOptions", + "type": "Object", + "tags": [], + "label": "routeDeprecationOptions", + "description": [], + "signature": [ + "RouteDeprecationInfo" + ], + "path": "packages/core/http/core-http-server/src/router/router.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-server.RouterDeprecatedRouteDetails.routeMethod", + "type": "CompoundType", + "tags": [], + "label": "routeMethod", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "server", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-server.SafeRouteMethod", + "text": "SafeRouteMethod" + }, + " | ", + { + "pluginId": "@kbn/core-http-server", + "scope": "server", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-server.DestructiveRouteMethod", + "text": "DestructiveRouteMethod" + } + ], + "path": "packages/core/http/core-http-server/src/router/router.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-server.RouterDeprecatedRouteDetails.routePath", + "type": "string", + "tags": [], + "label": "routePath", + "description": [], + "path": "packages/core/http/core-http-server/src/router/router.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-server.RouterDeprecatedRouteDetails.routeVersion", + "type": "string", + "tags": [], + "label": "routeVersion", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/http/core-http-server/src/router/router.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-http-server", "id": "def-server.RouterRoute", @@ -13711,6 +13900,20 @@ } ], "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-server.RouterRoute.isVersioned", + "type": "boolean", + "tags": [], + "label": "isVersioned", + "description": [], + "signature": [ + "false" + ], + "path": "packages/core/http/core-http-server/src/router/router.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -15784,6 +15987,10 @@ "plugin": "@kbn/core-http-router-server-internal", "path": "packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.test.ts" }, + { + "plugin": "@kbn/core-http-router-server-internal", + "path": "packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.test.ts" + }, { "plugin": "@kbn/core-http-router-server-internal", "path": "packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.test.ts" @@ -15879,7 +16086,7 @@ "section": "def-server.RouteConfigOptions", "text": "RouteConfigOptions" }, - ", \"security\" | \"description\" | \"deprecated\" | \"access\" | \"discontinued\"> | undefined; access: ", + ", \"security\" | \"description\" | \"summary\" | \"deprecated\" | \"access\" | \"discontinued\"> | undefined; access: ", { "pluginId": "@kbn/core-http-server", "scope": "server", @@ -15895,7 +16102,7 @@ "section": "def-server.RouteSecurity", "text": "RouteSecurity" }, - " | undefined; enableQueryVersion?: boolean | undefined; summary?: string | undefined; description?: string | undefined; deprecated?: boolean | undefined; discontinued?: string | undefined; }" + " | undefined; enableQueryVersion?: boolean | undefined; summary?: string | undefined; description?: string | undefined; discontinued?: string | undefined; }" ], "path": "packages/core/http/core-http-server/src/versioning/types.ts", "deprecated": false, @@ -16190,7 +16397,7 @@ "section": "def-server.RouteConfigOptions", "text": "RouteConfigOptions" }, - ", \"security\" | \"description\" | \"deprecated\" | \"access\" | \"discontinued\"> | undefined; access: ", + ", \"security\" | \"description\" | \"summary\" | \"deprecated\" | \"access\" | \"discontinued\"> | undefined; access: ", { "pluginId": "@kbn/core-http-server", "scope": "server", @@ -16206,7 +16413,7 @@ "section": "def-server.RouteSecurity", "text": "RouteSecurity" }, - " | undefined; enableQueryVersion?: boolean | undefined; summary?: string | undefined; description?: string | undefined; deprecated?: boolean | undefined; discontinued?: string | undefined; }" + " | undefined; enableQueryVersion?: boolean | undefined; summary?: string | undefined; description?: string | undefined; discontinued?: string | undefined; }" ], "path": "packages/core/http/core-http-server/src/versioning/types.ts", "deprecated": false, @@ -17333,7 +17540,7 @@ "section": "def-server.RouteConfigOptions", "text": "RouteConfigOptions" }, - ", \"security\" | \"description\" | \"deprecated\" | \"access\" | \"discontinued\"> | undefined; access: ", + ", \"security\" | \"description\" | \"summary\" | \"deprecated\" | \"access\" | \"discontinued\"> | undefined; access: ", { "pluginId": "@kbn/core-http-server", "scope": "server", @@ -17349,7 +17556,7 @@ "section": "def-server.RouteSecurity", "text": "RouteSecurity" }, - " | undefined; enableQueryVersion?: boolean | undefined; summary?: string | undefined; description?: string | undefined; deprecated?: boolean | undefined; discontinued?: string | undefined; }" + " | undefined; enableQueryVersion?: boolean | undefined; summary?: string | undefined; description?: string | undefined; discontinued?: string | undefined; }" ], "path": "packages/core/http/core-http-server/src/versioning/types.ts", "deprecated": false, @@ -17480,7 +17687,7 @@ "section": "def-server.RouteConfigOptions", "text": "RouteConfigOptions" }, - ", \"security\" | \"description\" | \"deprecated\" | \"access\" | \"discontinued\"> | undefined; access: ", + ", \"security\" | \"description\" | \"summary\" | \"deprecated\" | \"access\" | \"discontinued\"> | undefined; access: ", { "pluginId": "@kbn/core-http-server", "scope": "server", @@ -17496,7 +17703,7 @@ "section": "def-server.RouteSecurity", "text": "RouteSecurity" }, - " | undefined; enableQueryVersion?: boolean | undefined; summary?: string | undefined; description?: string | undefined; deprecated?: boolean | undefined; discontinued?: string | undefined; }" + " | undefined; enableQueryVersion?: boolean | undefined; summary?: string | undefined; description?: string | undefined; discontinued?: string | undefined; }" ], "path": "packages/core/http/core-http-server/src/versioning/types.ts", "deprecated": false, @@ -17779,7 +17986,7 @@ "section": "def-server.RouteConfigOptions", "text": "RouteConfigOptions" }, - ", \"security\" | \"description\" | \"deprecated\" | \"access\" | \"discontinued\"> | undefined; access: ", + ", \"security\" | \"description\" | \"summary\" | \"deprecated\" | \"access\" | \"discontinued\"> | undefined; access: ", { "pluginId": "@kbn/core-http-server", "scope": "server", @@ -17795,13 +18002,37 @@ "section": "def-server.RouteSecurity", "text": "RouteSecurity" }, - " | undefined; enableQueryVersion?: boolean | undefined; summary?: string | undefined; description?: string | undefined; deprecated?: boolean | undefined; discontinued?: string | undefined; }" + " | undefined; enableQueryVersion?: boolean | undefined; summary?: string | undefined; description?: string | undefined; discontinued?: string | undefined; }" ], "path": "packages/core/http/core-http-server/src/versioning/types.ts", "deprecated": false, "trackAdoption": false } ] + }, + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-server.VersionedRouter.getRoutes", + "type": "Function", + "tags": [], + "label": "getRoutes", + "description": [], + "signature": [ + "() => ", + { + "pluginId": "@kbn/core-http-server", + "scope": "server", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-server.VersionedRouterRoute", + "text": "VersionedRouterRoute" + }, + "[]" + ], + "path": "packages/core/http/core-http-server/src/versioning/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] } ], "initialIsOpen": false @@ -17861,6 +18092,158 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-server.VersionedRouterRoute", + "type": "Interface", + "tags": [], + "label": "VersionedRouterRoute", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "server", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-server.VersionedRouterRoute", + "text": "VersionedRouterRoute" + }, + "" + ], + "path": "packages/core/http/core-http-server/src/versioning/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-server.VersionedRouterRoute.method", + "type": "string", + "tags": [], + "label": "method", + "description": [], + "path": "packages/core/http/core-http-server/src/versioning/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-server.VersionedRouterRoute.path", + "type": "string", + "tags": [], + "label": "path", + "description": [], + "path": "packages/core/http/core-http-server/src/versioning/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-server.VersionedRouterRoute.options", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "{ options?: Omit<", + { + "pluginId": "@kbn/core-http-server", + "scope": "server", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-server.RouteConfigOptions", + "text": "RouteConfigOptions" + }, + "<", + { + "pluginId": "@kbn/core-http-server", + "scope": "server", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-server.RouteMethod", + "text": "RouteMethod" + }, + ">, \"security\" | \"description\" | \"summary\" | \"deprecated\" | \"access\" | \"discontinued\"> | undefined; security?: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "server", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-server.RouteSecurity", + "text": "RouteSecurity" + }, + " | undefined; description?: string | undefined; summary?: string | undefined; access: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "server", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-server.RouteAccess", + "text": "RouteAccess" + }, + "; discontinued?: string | undefined; enableQueryVersion?: boolean | undefined; }" + ], + "path": "packages/core/http/core-http-server/src/versioning/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-server.VersionedRouterRoute.handlers", + "type": "Array", + "tags": [], + "label": "handlers", + "description": [], + "signature": [ + "{ fn: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "server", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-server.RequestHandler", + "text": "RequestHandler" + }, + "; options: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "server", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-server.AddVersionOpts", + "text": "AddVersionOpts" + }, + "; }[]" + ], + "path": "packages/core/http/core-http-server/src/versioning/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-http-server", + "id": "def-server.VersionedRouterRoute.isVersioned", + "type": "boolean", + "tags": [], + "label": "isVersioned", + "description": [], + "signature": [ + "true" + ], + "path": "packages/core/http/core-http-server/src/versioning/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-http-server", "id": "def-server.VersionedRouteValidation", @@ -20680,7 +21063,7 @@ "section": "def-server.RouteConfigOptions", "text": "RouteConfigOptions" }, - ", \"security\" | \"description\" | \"deprecated\" | \"access\" | \"discontinued\"> | undefined; access: ", + ", \"security\" | \"description\" | \"summary\" | \"deprecated\" | \"access\" | \"discontinued\"> | undefined; access: ", { "pluginId": "@kbn/core-http-server", "scope": "server", @@ -20696,7 +21079,7 @@ "section": "def-server.RouteSecurity", "text": "RouteSecurity" }, - " | undefined; enableQueryVersion?: boolean | undefined; summary?: string | undefined; description?: string | undefined; deprecated?: boolean | undefined; discontinued?: string | undefined; }" + " | undefined; enableQueryVersion?: boolean | undefined; summary?: string | undefined; description?: string | undefined; discontinued?: string | undefined; }" ], "path": "packages/core/http/core-http-server/src/versioning/types.ts", "deprecated": false, @@ -20764,7 +21147,7 @@ "section": "def-server.RouteConfigOptions", "text": "RouteConfigOptions" }, - ", \"security\" | \"description\" | \"deprecated\" | \"access\" | \"discontinued\"> | undefined; access: ", + ", \"security\" | \"description\" | \"summary\" | \"deprecated\" | \"access\" | \"discontinued\"> | undefined; access: ", { "pluginId": "@kbn/core-http-server", "scope": "server", @@ -20780,7 +21163,7 @@ "section": "def-server.RouteSecurity", "text": "RouteSecurity" }, - " | undefined; enableQueryVersion?: boolean | undefined; summary?: string | undefined; description?: string | undefined; deprecated?: boolean | undefined; discontinued?: string | undefined; }" + " | undefined; enableQueryVersion?: boolean | undefined; summary?: string | undefined; description?: string | undefined; discontinued?: string | undefined; }" ], "path": "packages/core/http/core-http-server/src/versioning/types.ts", "deprecated": false, diff --git a/api_docs/kbn_core_http_server.mdx b/api_docs/kbn_core_http_server.mdx index 310bca83f6167..3cfc5bbdce292 100644 --- a/api_docs/kbn_core_http_server.mdx +++ b/api_docs/kbn_core_http_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server title: "@kbn/core-http-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server'] --- import kbnCoreHttpServerObj from './kbn_core_http_server.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 532 | 2 | 216 | 0 | +| 551 | 2 | 232 | 1 | ## Server diff --git a/api_docs/kbn_core_http_server_internal.mdx b/api_docs/kbn_core_http_server_internal.mdx index b3bd6f89fcb67..48a5e50cbf2a2 100644 --- a/api_docs/kbn_core_http_server_internal.mdx +++ b/api_docs/kbn_core_http_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-internal title: "@kbn/core-http-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-internal'] --- import kbnCoreHttpServerInternalObj from './kbn_core_http_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_mocks.devdocs.json b/api_docs/kbn_core_http_server_mocks.devdocs.json index c395e16fc2bb2..ec3fe801a5673 100644 --- a/api_docs/kbn_core_http_server_mocks.devdocs.json +++ b/api_docs/kbn_core_http_server_mocks.devdocs.json @@ -332,7 +332,15 @@ "section": "def-server.OnPreResponseHandler", "text": "OnPreResponseHandler" }, - "], unknown>; } & Omit<", + "], unknown>; getDeprecatedRoutes: jest.MockInstance<", + { + "pluginId": "@kbn/core-http-server", + "scope": "server", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-server.RouterDeprecatedRouteDetails", + "text": "RouterDeprecatedRouteDetails" + }, + "[], [], unknown>; } & Omit<", { "pluginId": "@kbn/core-http-server", "scope": "server", @@ -619,7 +627,19 @@ "section": "def-server.OnPreResponseHandler", "text": "OnPreResponseHandler" }, - "], unknown>; registerRouterAfterListening: jest.MockInstance; getDeprecatedRoutes: jest.MockInstance<", + { + "pluginId": "@kbn/core-http-server", + "scope": "server", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-server.RouterDeprecatedRouteDetails", + "text": "RouterDeprecatedRouteDetails" + }, + "[], [], unknown>; registerOnPostValidation: jest.MockInstance, metadata: { deprecated: ", + "RouteDeprecationInfo", + "; }) => void], unknown>; registerRouterAfterListening: jest.MockInstance], unknown>; } & Omit<", + ">], unknown>; getRegisteredDeprecatedApis: jest.MockInstance<", + { + "pluginId": "@kbn/core-http-server", + "scope": "server", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-server.RouterDeprecatedRouteDetails", + "text": "RouterDeprecatedRouteDetails" + }, + "[], [], unknown>; } & Omit<", "InternalHttpServiceSetup", ", \"createRouter\" | \"basePath\" | \"auth\" | \"staticAssets\" | \"authRequestHeaders\"> & { auth: AuthMocked; basePath: BasePathMocked; staticAssets: InternalStaticAssetsMocked; createRouter: jest.MockedFunction<(path: string) => ", { diff --git a/api_docs/kbn_core_http_server_mocks.mdx b/api_docs/kbn_core_http_server_mocks.mdx index c6ba1644a2b93..3793a797714ba 100644 --- a/api_docs/kbn_core_http_server_mocks.mdx +++ b/api_docs/kbn_core_http_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-mocks title: "@kbn/core-http-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-mocks'] --- import kbnCoreHttpServerMocksObj from './kbn_core_http_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser.mdx b/api_docs/kbn_core_i18n_browser.mdx index b192cf59c52d5..16eb6cd54bc4a 100644 --- a/api_docs/kbn_core_i18n_browser.mdx +++ b/api_docs/kbn_core_i18n_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser title: "@kbn/core-i18n-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser'] --- import kbnCoreI18nBrowserObj from './kbn_core_i18n_browser.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser_mocks.mdx b/api_docs/kbn_core_i18n_browser_mocks.mdx index d726785afc144..5cbe272cf84d3 100644 --- a/api_docs/kbn_core_i18n_browser_mocks.mdx +++ b/api_docs/kbn_core_i18n_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser-mocks title: "@kbn/core-i18n-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser-mocks'] --- import kbnCoreI18nBrowserMocksObj from './kbn_core_i18n_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server.mdx b/api_docs/kbn_core_i18n_server.mdx index 903a96a71744c..f909b471e70dd 100644 --- a/api_docs/kbn_core_i18n_server.mdx +++ b/api_docs/kbn_core_i18n_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server title: "@kbn/core-i18n-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server'] --- import kbnCoreI18nServerObj from './kbn_core_i18n_server.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_internal.mdx b/api_docs/kbn_core_i18n_server_internal.mdx index efde74b88af17..5d6b70474e33f 100644 --- a/api_docs/kbn_core_i18n_server_internal.mdx +++ b/api_docs/kbn_core_i18n_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-internal title: "@kbn/core-i18n-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-internal'] --- import kbnCoreI18nServerInternalObj from './kbn_core_i18n_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_mocks.mdx b/api_docs/kbn_core_i18n_server_mocks.mdx index 0ce740eb8b514..93820eb8d5c89 100644 --- a/api_docs/kbn_core_i18n_server_mocks.mdx +++ b/api_docs/kbn_core_i18n_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-mocks title: "@kbn/core-i18n-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-mocks'] --- import kbnCoreI18nServerMocksObj from './kbn_core_i18n_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx index 9dede564a4bff..1378fea456c64 100644 --- a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx +++ b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser-mocks title: "@kbn/core-injected-metadata-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser-mocks'] --- import kbnCoreInjectedMetadataBrowserMocksObj from './kbn_core_injected_metadata_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_internal.mdx b/api_docs/kbn_core_integrations_browser_internal.mdx index 5c2d5ab9fd84a..81587ac6062ac 100644 --- a/api_docs/kbn_core_integrations_browser_internal.mdx +++ b/api_docs/kbn_core_integrations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-internal title: "@kbn/core-integrations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-internal'] --- import kbnCoreIntegrationsBrowserInternalObj from './kbn_core_integrations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_mocks.mdx b/api_docs/kbn_core_integrations_browser_mocks.mdx index aed8d407def2b..996f6ee99ccde 100644 --- a/api_docs/kbn_core_integrations_browser_mocks.mdx +++ b/api_docs/kbn_core_integrations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-mocks title: "@kbn/core-integrations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-mocks'] --- import kbnCoreIntegrationsBrowserMocksObj from './kbn_core_integrations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser.mdx b/api_docs/kbn_core_lifecycle_browser.mdx index 7b49cb34c5dd2..3766da8aafae4 100644 --- a/api_docs/kbn_core_lifecycle_browser.mdx +++ b/api_docs/kbn_core_lifecycle_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser title: "@kbn/core-lifecycle-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser'] --- import kbnCoreLifecycleBrowserObj from './kbn_core_lifecycle_browser.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser_mocks.mdx b/api_docs/kbn_core_lifecycle_browser_mocks.mdx index baf0c92aba030..fc71973cb1c0e 100644 --- a/api_docs/kbn_core_lifecycle_browser_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser-mocks title: "@kbn/core-lifecycle-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser-mocks'] --- import kbnCoreLifecycleBrowserMocksObj from './kbn_core_lifecycle_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server.mdx b/api_docs/kbn_core_lifecycle_server.mdx index f1f312421fcc9..675c84509790e 100644 --- a/api_docs/kbn_core_lifecycle_server.mdx +++ b/api_docs/kbn_core_lifecycle_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server title: "@kbn/core-lifecycle-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server'] --- import kbnCoreLifecycleServerObj from './kbn_core_lifecycle_server.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server_mocks.mdx b/api_docs/kbn_core_lifecycle_server_mocks.mdx index 214d13ceafbc7..067edc1860641 100644 --- a/api_docs/kbn_core_lifecycle_server_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server-mocks title: "@kbn/core-lifecycle-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server-mocks'] --- import kbnCoreLifecycleServerMocksObj from './kbn_core_lifecycle_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_browser_mocks.mdx b/api_docs/kbn_core_logging_browser_mocks.mdx index d874b1f458227..a47aa4a76e351 100644 --- a/api_docs/kbn_core_logging_browser_mocks.mdx +++ b/api_docs/kbn_core_logging_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-browser-mocks title: "@kbn/core-logging-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-browser-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-browser-mocks'] --- import kbnCoreLoggingBrowserMocksObj from './kbn_core_logging_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_common_internal.mdx b/api_docs/kbn_core_logging_common_internal.mdx index 6db909ca3fe21..d85f960999710 100644 --- a/api_docs/kbn_core_logging_common_internal.mdx +++ b/api_docs/kbn_core_logging_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-common-internal title: "@kbn/core-logging-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-common-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-common-internal'] --- import kbnCoreLoggingCommonInternalObj from './kbn_core_logging_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index ce91cb57aa141..6115724409c9e 100644 --- a/api_docs/kbn_core_logging_server.mdx +++ b/api_docs/kbn_core_logging_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server title: "@kbn/core-logging-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server'] --- import kbnCoreLoggingServerObj from './kbn_core_logging_server.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_internal.mdx b/api_docs/kbn_core_logging_server_internal.mdx index a86a005634d7b..acc13e824320a 100644 --- a/api_docs/kbn_core_logging_server_internal.mdx +++ b/api_docs/kbn_core_logging_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-internal title: "@kbn/core-logging-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-internal'] --- import kbnCoreLoggingServerInternalObj from './kbn_core_logging_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_mocks.mdx b/api_docs/kbn_core_logging_server_mocks.mdx index 8f50f19dd0ef2..ad7ee90d697b8 100644 --- a/api_docs/kbn_core_logging_server_mocks.mdx +++ b/api_docs/kbn_core_logging_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-mocks title: "@kbn/core-logging-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-mocks'] --- import kbnCoreLoggingServerMocksObj from './kbn_core_logging_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_internal.mdx b/api_docs/kbn_core_metrics_collectors_server_internal.mdx index 91ea123b21c22..a2c141f9bb48a 100644 --- a/api_docs/kbn_core_metrics_collectors_server_internal.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-internal title: "@kbn/core-metrics-collectors-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-internal'] --- import kbnCoreMetricsCollectorsServerInternalObj from './kbn_core_metrics_collectors_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx index f28a204a8f71a..82f34b2379b1e 100644 --- a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-mocks title: "@kbn/core-metrics-collectors-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-mocks'] --- import kbnCoreMetricsCollectorsServerMocksObj from './kbn_core_metrics_collectors_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server.mdx b/api_docs/kbn_core_metrics_server.mdx index dfb1370f700a7..7b279622c89c8 100644 --- a/api_docs/kbn_core_metrics_server.mdx +++ b/api_docs/kbn_core_metrics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server title: "@kbn/core-metrics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server'] --- import kbnCoreMetricsServerObj from './kbn_core_metrics_server.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_internal.mdx b/api_docs/kbn_core_metrics_server_internal.mdx index c60a4ecf318e9..0eab62a34c347 100644 --- a/api_docs/kbn_core_metrics_server_internal.mdx +++ b/api_docs/kbn_core_metrics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-internal title: "@kbn/core-metrics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-internal'] --- import kbnCoreMetricsServerInternalObj from './kbn_core_metrics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_mocks.mdx b/api_docs/kbn_core_metrics_server_mocks.mdx index 66c80b4c98e86..9e17541707258 100644 --- a/api_docs/kbn_core_metrics_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-mocks title: "@kbn/core-metrics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-mocks'] --- import kbnCoreMetricsServerMocksObj from './kbn_core_metrics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_mount_utils_browser.mdx b/api_docs/kbn_core_mount_utils_browser.mdx index b645305c99a17..5a134ed3b2a9f 100644 --- a/api_docs/kbn_core_mount_utils_browser.mdx +++ b/api_docs/kbn_core_mount_utils_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-mount-utils-browser title: "@kbn/core-mount-utils-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-mount-utils-browser plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-mount-utils-browser'] --- import kbnCoreMountUtilsBrowserObj from './kbn_core_mount_utils_browser.devdocs.json'; diff --git a/api_docs/kbn_core_node_server.mdx b/api_docs/kbn_core_node_server.mdx index 215cf8ede16f5..3a06f2adbab3f 100644 --- a/api_docs/kbn_core_node_server.mdx +++ b/api_docs/kbn_core_node_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server title: "@kbn/core-node-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server'] --- import kbnCoreNodeServerObj from './kbn_core_node_server.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_internal.mdx b/api_docs/kbn_core_node_server_internal.mdx index 7d6b2a0e77b60..481f6598c3aaa 100644 --- a/api_docs/kbn_core_node_server_internal.mdx +++ b/api_docs/kbn_core_node_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-internal title: "@kbn/core-node-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-internal'] --- import kbnCoreNodeServerInternalObj from './kbn_core_node_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_mocks.mdx b/api_docs/kbn_core_node_server_mocks.mdx index 295fab7feb119..9be973ce9bfea 100644 --- a/api_docs/kbn_core_node_server_mocks.mdx +++ b/api_docs/kbn_core_node_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-mocks title: "@kbn/core-node-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-mocks'] --- import kbnCoreNodeServerMocksObj from './kbn_core_node_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser.mdx b/api_docs/kbn_core_notifications_browser.mdx index 9fb84fa7ca548..a2f75e37c59e0 100644 --- a/api_docs/kbn_core_notifications_browser.mdx +++ b/api_docs/kbn_core_notifications_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser title: "@kbn/core-notifications-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser'] --- import kbnCoreNotificationsBrowserObj from './kbn_core_notifications_browser.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_internal.mdx b/api_docs/kbn_core_notifications_browser_internal.mdx index ff5524fa97b31..00ef0fc184366 100644 --- a/api_docs/kbn_core_notifications_browser_internal.mdx +++ b/api_docs/kbn_core_notifications_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-internal title: "@kbn/core-notifications-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-internal'] --- import kbnCoreNotificationsBrowserInternalObj from './kbn_core_notifications_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_mocks.mdx b/api_docs/kbn_core_notifications_browser_mocks.mdx index c052e236c1a14..6fd26fe8edd0e 100644 --- a/api_docs/kbn_core_notifications_browser_mocks.mdx +++ b/api_docs/kbn_core_notifications_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-mocks title: "@kbn/core-notifications-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-mocks'] --- import kbnCoreNotificationsBrowserMocksObj from './kbn_core_notifications_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser.mdx b/api_docs/kbn_core_overlays_browser.mdx index 9d6a66ea44d8a..80aeef9a0b5cc 100644 --- a/api_docs/kbn_core_overlays_browser.mdx +++ b/api_docs/kbn_core_overlays_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser title: "@kbn/core-overlays-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser'] --- import kbnCoreOverlaysBrowserObj from './kbn_core_overlays_browser.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_internal.mdx b/api_docs/kbn_core_overlays_browser_internal.mdx index fa29aa630cdc2..85615b191c104 100644 --- a/api_docs/kbn_core_overlays_browser_internal.mdx +++ b/api_docs/kbn_core_overlays_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-internal title: "@kbn/core-overlays-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-internal'] --- import kbnCoreOverlaysBrowserInternalObj from './kbn_core_overlays_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_mocks.mdx b/api_docs/kbn_core_overlays_browser_mocks.mdx index 88023e8da1cab..af85e91701405 100644 --- a/api_docs/kbn_core_overlays_browser_mocks.mdx +++ b/api_docs/kbn_core_overlays_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-mocks title: "@kbn/core-overlays-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-mocks'] --- import kbnCoreOverlaysBrowserMocksObj from './kbn_core_overlays_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser.mdx b/api_docs/kbn_core_plugins_browser.mdx index 82c83c2239bf6..e938f81e2007f 100644 --- a/api_docs/kbn_core_plugins_browser.mdx +++ b/api_docs/kbn_core_plugins_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser title: "@kbn/core-plugins-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser'] --- import kbnCorePluginsBrowserObj from './kbn_core_plugins_browser.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser_mocks.mdx b/api_docs/kbn_core_plugins_browser_mocks.mdx index 5a8b51b267740..1d0b8dcce372d 100644 --- a/api_docs/kbn_core_plugins_browser_mocks.mdx +++ b/api_docs/kbn_core_plugins_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser-mocks title: "@kbn/core-plugins-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser-mocks'] --- import kbnCorePluginsBrowserMocksObj from './kbn_core_plugins_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_contracts_browser.mdx b/api_docs/kbn_core_plugins_contracts_browser.mdx index 69013b512b748..02738d2f74e78 100644 --- a/api_docs/kbn_core_plugins_contracts_browser.mdx +++ b/api_docs/kbn_core_plugins_contracts_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-contracts-browser title: "@kbn/core-plugins-contracts-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-contracts-browser plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-contracts-browser'] --- import kbnCorePluginsContractsBrowserObj from './kbn_core_plugins_contracts_browser.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_contracts_server.mdx b/api_docs/kbn_core_plugins_contracts_server.mdx index f7eddb43b0ded..317604117eb70 100644 --- a/api_docs/kbn_core_plugins_contracts_server.mdx +++ b/api_docs/kbn_core_plugins_contracts_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-contracts-server title: "@kbn/core-plugins-contracts-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-contracts-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-contracts-server'] --- import kbnCorePluginsContractsServerObj from './kbn_core_plugins_contracts_server.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server.mdx b/api_docs/kbn_core_plugins_server.mdx index 1b1c6371a97bf..a33d03e46898d 100644 --- a/api_docs/kbn_core_plugins_server.mdx +++ b/api_docs/kbn_core_plugins_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server title: "@kbn/core-plugins-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server'] --- import kbnCorePluginsServerObj from './kbn_core_plugins_server.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server_mocks.mdx b/api_docs/kbn_core_plugins_server_mocks.mdx index 853cf30782fd4..b4762dde256cd 100644 --- a/api_docs/kbn_core_plugins_server_mocks.mdx +++ b/api_docs/kbn_core_plugins_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server-mocks title: "@kbn/core-plugins-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server-mocks'] --- import kbnCorePluginsServerMocksObj from './kbn_core_plugins_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server.mdx b/api_docs/kbn_core_preboot_server.mdx index 24f740fb06a2c..c6a001d0fbf84 100644 --- a/api_docs/kbn_core_preboot_server.mdx +++ b/api_docs/kbn_core_preboot_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server title: "@kbn/core-preboot-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server'] --- import kbnCorePrebootServerObj from './kbn_core_preboot_server.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server_mocks.mdx b/api_docs/kbn_core_preboot_server_mocks.mdx index 857c465f543fc..09c4eb802b98b 100644 --- a/api_docs/kbn_core_preboot_server_mocks.mdx +++ b/api_docs/kbn_core_preboot_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server-mocks title: "@kbn/core-preboot-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server-mocks'] --- import kbnCorePrebootServerMocksObj from './kbn_core_preboot_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_browser_mocks.mdx b/api_docs/kbn_core_rendering_browser_mocks.mdx index 5d3c77fefa320..b2a076aab3896 100644 --- a/api_docs/kbn_core_rendering_browser_mocks.mdx +++ b/api_docs/kbn_core_rendering_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-browser-mocks title: "@kbn/core-rendering-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-browser-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-browser-mocks'] --- import kbnCoreRenderingBrowserMocksObj from './kbn_core_rendering_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_internal.mdx b/api_docs/kbn_core_rendering_server_internal.mdx index 72a433302e87d..ec8aaf2dbd646 100644 --- a/api_docs/kbn_core_rendering_server_internal.mdx +++ b/api_docs/kbn_core_rendering_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-internal title: "@kbn/core-rendering-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-internal'] --- import kbnCoreRenderingServerInternalObj from './kbn_core_rendering_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_mocks.mdx b/api_docs/kbn_core_rendering_server_mocks.mdx index cf042c9d2f889..f1dde07e72cac 100644 --- a/api_docs/kbn_core_rendering_server_mocks.mdx +++ b/api_docs/kbn_core_rendering_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-mocks title: "@kbn/core-rendering-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-mocks'] --- import kbnCoreRenderingServerMocksObj from './kbn_core_rendering_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_root_server_internal.mdx b/api_docs/kbn_core_root_server_internal.mdx index 86846efd64498..13341ccd8d403 100644 --- a/api_docs/kbn_core_root_server_internal.mdx +++ b/api_docs/kbn_core_root_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-root-server-internal title: "@kbn/core-root-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-root-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-root-server-internal'] --- import kbnCoreRootServerInternalObj from './kbn_core_root_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index e9a861d116ecd..25f8cb87b40e7 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.mdx +++ b/api_docs/kbn_core_saved_objects_api_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-browser title: "@kbn/core-saved-objects-api-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-browser plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-browser'] --- import kbnCoreSavedObjectsApiBrowserObj from './kbn_core_saved_objects_api_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server.mdx b/api_docs/kbn_core_saved_objects_api_server.mdx index 8b9e63c123248..f7878cd496e33 100644 --- a/api_docs/kbn_core_saved_objects_api_server.mdx +++ b/api_docs/kbn_core_saved_objects_api_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server title: "@kbn/core-saved-objects-api-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server'] --- import kbnCoreSavedObjectsApiServerObj from './kbn_core_saved_objects_api_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx index 8e745268e94d9..74ef74b4c0894 100644 --- a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-mocks title: "@kbn/core-saved-objects-api-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-mocks'] --- import kbnCoreSavedObjectsApiServerMocksObj from './kbn_core_saved_objects_api_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_internal.mdx b/api_docs/kbn_core_saved_objects_base_server_internal.mdx index dcbee781dad94..904d1a5c73bd3 100644 --- a/api_docs/kbn_core_saved_objects_base_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-internal title: "@kbn/core-saved-objects-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-internal'] --- import kbnCoreSavedObjectsBaseServerInternalObj from './kbn_core_saved_objects_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx index b99bbe4dbbae2..b8ff19201e047 100644 --- a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-mocks title: "@kbn/core-saved-objects-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-mocks'] --- import kbnCoreSavedObjectsBaseServerMocksObj from './kbn_core_saved_objects_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser.mdx b/api_docs/kbn_core_saved_objects_browser.mdx index 56780e4c5c100..5e82279f2ff86 100644 --- a/api_docs/kbn_core_saved_objects_browser.mdx +++ b/api_docs/kbn_core_saved_objects_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser title: "@kbn/core-saved-objects-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser'] --- import kbnCoreSavedObjectsBrowserObj from './kbn_core_saved_objects_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_internal.mdx b/api_docs/kbn_core_saved_objects_browser_internal.mdx index 4cc71746eae52..82f61ada0343d 100644 --- a/api_docs/kbn_core_saved_objects_browser_internal.mdx +++ b/api_docs/kbn_core_saved_objects_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-internal title: "@kbn/core-saved-objects-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-internal'] --- import kbnCoreSavedObjectsBrowserInternalObj from './kbn_core_saved_objects_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_mocks.mdx b/api_docs/kbn_core_saved_objects_browser_mocks.mdx index 8e9920f59aa3d..1d8a0a080e148 100644 --- a/api_docs/kbn_core_saved_objects_browser_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-mocks title: "@kbn/core-saved-objects-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-mocks'] --- import kbnCoreSavedObjectsBrowserMocksObj from './kbn_core_saved_objects_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_common.mdx b/api_docs/kbn_core_saved_objects_common.mdx index bdcd3b2ddf551..c640abd7302e3 100644 --- a/api_docs/kbn_core_saved_objects_common.mdx +++ b/api_docs/kbn_core_saved_objects_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-common title: "@kbn/core-saved-objects-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-common plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-common'] --- import kbnCoreSavedObjectsCommonObj from './kbn_core_saved_objects_common.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx index 987311fb0fec7..061a0f16624d9 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-internal title: "@kbn/core-saved-objects-import-export-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-internal'] --- import kbnCoreSavedObjectsImportExportServerInternalObj from './kbn_core_saved_objects_import_export_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx index 7092a5999a571..992856657c001 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-mocks title: "@kbn/core-saved-objects-import-export-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-mocks'] --- import kbnCoreSavedObjectsImportExportServerMocksObj from './kbn_core_saved_objects_import_export_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx index 7bb787d2bda8b..c30b20912f219 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-internal title: "@kbn/core-saved-objects-migration-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-internal'] --- import kbnCoreSavedObjectsMigrationServerInternalObj from './kbn_core_saved_objects_migration_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx index 04c4525bea097..26b59b25e3340 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-mocks title: "@kbn/core-saved-objects-migration-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-mocks'] --- import kbnCoreSavedObjectsMigrationServerMocksObj from './kbn_core_saved_objects_migration_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server.mdx b/api_docs/kbn_core_saved_objects_server.mdx index 15954be7fa236..49d9a6cd3fd72 100644 --- a/api_docs/kbn_core_saved_objects_server.mdx +++ b/api_docs/kbn_core_saved_objects_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server title: "@kbn/core-saved-objects-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server'] --- import kbnCoreSavedObjectsServerObj from './kbn_core_saved_objects_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server_internal.mdx b/api_docs/kbn_core_saved_objects_server_internal.mdx index 35f7a1cb303d4..c97eb121023a0 100644 --- a/api_docs/kbn_core_saved_objects_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-internal title: "@kbn/core-saved-objects-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-internal'] --- import kbnCoreSavedObjectsServerInternalObj from './kbn_core_saved_objects_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server_mocks.mdx b/api_docs/kbn_core_saved_objects_server_mocks.mdx index bc483ee36f6a7..f71529dae07a7 100644 --- a/api_docs/kbn_core_saved_objects_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-mocks title: "@kbn/core-saved-objects-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-mocks'] --- import kbnCoreSavedObjectsServerMocksObj from './kbn_core_saved_objects_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_utils_server.mdx b/api_docs/kbn_core_saved_objects_utils_server.mdx index 803d37ebde085..f96934f4c0960 100644 --- a/api_docs/kbn_core_saved_objects_utils_server.mdx +++ b/api_docs/kbn_core_saved_objects_utils_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-utils-server title: "@kbn/core-saved-objects-utils-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-utils-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-utils-server'] --- import kbnCoreSavedObjectsUtilsServerObj from './kbn_core_saved_objects_utils_server.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser.mdx b/api_docs/kbn_core_security_browser.mdx index 96dd119cef6fa..e92b1d7332c50 100644 --- a/api_docs/kbn_core_security_browser.mdx +++ b/api_docs/kbn_core_security_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser title: "@kbn/core-security-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser'] --- import kbnCoreSecurityBrowserObj from './kbn_core_security_browser.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser_internal.mdx b/api_docs/kbn_core_security_browser_internal.mdx index eac5af7cb5269..b7d6b7da289e1 100644 --- a/api_docs/kbn_core_security_browser_internal.mdx +++ b/api_docs/kbn_core_security_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser-internal title: "@kbn/core-security-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser-internal'] --- import kbnCoreSecurityBrowserInternalObj from './kbn_core_security_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser_mocks.mdx b/api_docs/kbn_core_security_browser_mocks.mdx index a95e8b23cd920..c455b4cccc2fb 100644 --- a/api_docs/kbn_core_security_browser_mocks.mdx +++ b/api_docs/kbn_core_security_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser-mocks title: "@kbn/core-security-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser-mocks'] --- import kbnCoreSecurityBrowserMocksObj from './kbn_core_security_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_security_common.mdx b/api_docs/kbn_core_security_common.mdx index 9e9b68a6e6b7a..99e04cfb530b9 100644 --- a/api_docs/kbn_core_security_common.mdx +++ b/api_docs/kbn_core_security_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-common title: "@kbn/core-security-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-common plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-common'] --- import kbnCoreSecurityCommonObj from './kbn_core_security_common.devdocs.json'; diff --git a/api_docs/kbn_core_security_server.mdx b/api_docs/kbn_core_security_server.mdx index a3389b5621c3e..a360d6e4fd9ae 100644 --- a/api_docs/kbn_core_security_server.mdx +++ b/api_docs/kbn_core_security_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server title: "@kbn/core-security-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server'] --- import kbnCoreSecurityServerObj from './kbn_core_security_server.devdocs.json'; diff --git a/api_docs/kbn_core_security_server_internal.mdx b/api_docs/kbn_core_security_server_internal.mdx index ab84b4111d703..ba837b3b8baee 100644 --- a/api_docs/kbn_core_security_server_internal.mdx +++ b/api_docs/kbn_core_security_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server-internal title: "@kbn/core-security-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server-internal'] --- import kbnCoreSecurityServerInternalObj from './kbn_core_security_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_security_server_mocks.mdx b/api_docs/kbn_core_security_server_mocks.mdx index ab3aac0242295..130e01e34a474 100644 --- a/api_docs/kbn_core_security_server_mocks.mdx +++ b/api_docs/kbn_core_security_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server-mocks title: "@kbn/core-security-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server-mocks'] --- import kbnCoreSecurityServerMocksObj from './kbn_core_security_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_status_common.mdx b/api_docs/kbn_core_status_common.mdx index 63b42c13688fb..01de7991a4162 100644 --- a/api_docs/kbn_core_status_common.mdx +++ b/api_docs/kbn_core_status_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common title: "@kbn/core-status-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common'] --- import kbnCoreStatusCommonObj from './kbn_core_status_common.devdocs.json'; diff --git a/api_docs/kbn_core_status_common_internal.mdx b/api_docs/kbn_core_status_common_internal.mdx index 54a311e611490..1ec92f65eb805 100644 --- a/api_docs/kbn_core_status_common_internal.mdx +++ b/api_docs/kbn_core_status_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common-internal title: "@kbn/core-status-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common-internal'] --- import kbnCoreStatusCommonInternalObj from './kbn_core_status_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server.mdx b/api_docs/kbn_core_status_server.mdx index cc86508d7eb56..39870e5beaaba 100644 --- a/api_docs/kbn_core_status_server.mdx +++ b/api_docs/kbn_core_status_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server title: "@kbn/core-status-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server'] --- import kbnCoreStatusServerObj from './kbn_core_status_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_internal.mdx b/api_docs/kbn_core_status_server_internal.mdx index 4258bc8651f57..73100e126d083 100644 --- a/api_docs/kbn_core_status_server_internal.mdx +++ b/api_docs/kbn_core_status_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-internal title: "@kbn/core-status-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-internal'] --- import kbnCoreStatusServerInternalObj from './kbn_core_status_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_mocks.mdx b/api_docs/kbn_core_status_server_mocks.mdx index 81ab412672316..196ae614d7c7d 100644 --- a/api_docs/kbn_core_status_server_mocks.mdx +++ b/api_docs/kbn_core_status_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-mocks title: "@kbn/core-status-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-mocks'] --- import kbnCoreStatusServerMocksObj from './kbn_core_status_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx index 5f875612ad810..bbfbf3ec39811 100644 --- a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx +++ b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-deprecations-getters title: "@kbn/core-test-helpers-deprecations-getters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-deprecations-getters plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-deprecations-getters'] --- import kbnCoreTestHelpersDeprecationsGettersObj from './kbn_core_test_helpers_deprecations_getters.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx index 3d26ea9327ad5..49e728a8f7ee4 100644 --- a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx +++ b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-http-setup-browser title: "@kbn/core-test-helpers-http-setup-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-http-setup-browser plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-http-setup-browser'] --- import kbnCoreTestHelpersHttpSetupBrowserObj from './kbn_core_test_helpers_http_setup_browser.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_kbn_server.mdx b/api_docs/kbn_core_test_helpers_kbn_server.mdx index 92a02d7608c7a..5144cb6e6f970 100644 --- a/api_docs/kbn_core_test_helpers_kbn_server.mdx +++ b/api_docs/kbn_core_test_helpers_kbn_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-kbn-server title: "@kbn/core-test-helpers-kbn-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-kbn-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-kbn-server'] --- import kbnCoreTestHelpersKbnServerObj from './kbn_core_test_helpers_kbn_server.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_model_versions.mdx b/api_docs/kbn_core_test_helpers_model_versions.mdx index 5bdb8b4a1c88d..f3fb8f2ef0f85 100644 --- a/api_docs/kbn_core_test_helpers_model_versions.mdx +++ b/api_docs/kbn_core_test_helpers_model_versions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-model-versions title: "@kbn/core-test-helpers-model-versions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-model-versions plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-model-versions'] --- import kbnCoreTestHelpersModelVersionsObj from './kbn_core_test_helpers_model_versions.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx index 77b07f001f37a..d3b44ce5edeac 100644 --- a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx +++ b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-so-type-serializer title: "@kbn/core-test-helpers-so-type-serializer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-so-type-serializer plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-so-type-serializer'] --- import kbnCoreTestHelpersSoTypeSerializerObj from './kbn_core_test_helpers_so_type_serializer.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_test_utils.mdx b/api_docs/kbn_core_test_helpers_test_utils.mdx index b7ed5c5f680b6..265c66d935333 100644 --- a/api_docs/kbn_core_test_helpers_test_utils.mdx +++ b/api_docs/kbn_core_test_helpers_test_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-test-utils title: "@kbn/core-test-helpers-test-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-test-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-test-utils'] --- import kbnCoreTestHelpersTestUtilsObj from './kbn_core_test_helpers_test_utils.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser.mdx b/api_docs/kbn_core_theme_browser.mdx index b9976b32c38d3..a5219fe5e8ab2 100644 --- a/api_docs/kbn_core_theme_browser.mdx +++ b/api_docs/kbn_core_theme_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser title: "@kbn/core-theme-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser'] --- import kbnCoreThemeBrowserObj from './kbn_core_theme_browser.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index 4b168d7ddddd3..89d859f973f69 100644 --- a/api_docs/kbn_core_theme_browser_mocks.mdx +++ b/api_docs/kbn_core_theme_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-mocks title: "@kbn/core-theme-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-mocks'] --- import kbnCoreThemeBrowserMocksObj from './kbn_core_theme_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser.mdx b/api_docs/kbn_core_ui_settings_browser.mdx index 2366f3016476f..d95034fd001a3 100644 --- a/api_docs/kbn_core_ui_settings_browser.mdx +++ b/api_docs/kbn_core_ui_settings_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser title: "@kbn/core-ui-settings-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser'] --- import kbnCoreUiSettingsBrowserObj from './kbn_core_ui_settings_browser.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_internal.mdx b/api_docs/kbn_core_ui_settings_browser_internal.mdx index 3363c571a7146..0b229a86bd042 100644 --- a/api_docs/kbn_core_ui_settings_browser_internal.mdx +++ b/api_docs/kbn_core_ui_settings_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-internal title: "@kbn/core-ui-settings-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-internal'] --- import kbnCoreUiSettingsBrowserInternalObj from './kbn_core_ui_settings_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_mocks.mdx b/api_docs/kbn_core_ui_settings_browser_mocks.mdx index 9771d40aed460..190a865f60254 100644 --- a/api_docs/kbn_core_ui_settings_browser_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-mocks title: "@kbn/core-ui-settings-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-mocks'] --- import kbnCoreUiSettingsBrowserMocksObj from './kbn_core_ui_settings_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_common.mdx b/api_docs/kbn_core_ui_settings_common.mdx index 9c5b51b4ccd15..22d022b4dedca 100644 --- a/api_docs/kbn_core_ui_settings_common.mdx +++ b/api_docs/kbn_core_ui_settings_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-common title: "@kbn/core-ui-settings-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-common plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-common'] --- import kbnCoreUiSettingsCommonObj from './kbn_core_ui_settings_common.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server.mdx b/api_docs/kbn_core_ui_settings_server.mdx index 54ac82fdd53ea..a6a4e491aad3b 100644 --- a/api_docs/kbn_core_ui_settings_server.mdx +++ b/api_docs/kbn_core_ui_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server title: "@kbn/core-ui-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server'] --- import kbnCoreUiSettingsServerObj from './kbn_core_ui_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_internal.mdx b/api_docs/kbn_core_ui_settings_server_internal.mdx index 571ae8ebf2a08..478448114cc24 100644 --- a/api_docs/kbn_core_ui_settings_server_internal.mdx +++ b/api_docs/kbn_core_ui_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-internal title: "@kbn/core-ui-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-internal'] --- import kbnCoreUiSettingsServerInternalObj from './kbn_core_ui_settings_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_mocks.mdx b/api_docs/kbn_core_ui_settings_server_mocks.mdx index 8a33bd85a73b8..a0501fe3df557 100644 --- a/api_docs/kbn_core_ui_settings_server_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-mocks title: "@kbn/core-ui-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-mocks'] --- import kbnCoreUiSettingsServerMocksObj from './kbn_core_ui_settings_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server.devdocs.json b/api_docs/kbn_core_usage_data_server.devdocs.json index 4ba0e9204206d..f9604e3bebfc9 100644 --- a/api_docs/kbn_core_usage_data_server.devdocs.json +++ b/api_docs/kbn_core_usage_data_server.devdocs.json @@ -98,6 +98,75 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/core-usage-data-server", + "id": "def-server.CoreDeprecatedApiUsageStats", + "type": "Interface", + "tags": [], + "label": "CoreDeprecatedApiUsageStats", + "description": [], + "path": "packages/core/usage-data/core-usage-data-server/src/core_usage_stats.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-usage-data-server", + "id": "def-server.CoreDeprecatedApiUsageStats.apiId", + "type": "string", + "tags": [], + "label": "apiId", + "description": [], + "path": "packages/core/usage-data/core-usage-data-server/src/core_usage_stats.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-usage-data-server", + "id": "def-server.CoreDeprecatedApiUsageStats.totalMarkedAsResolved", + "type": "number", + "tags": [], + "label": "totalMarkedAsResolved", + "description": [], + "path": "packages/core/usage-data/core-usage-data-server/src/core_usage_stats.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-usage-data-server", + "id": "def-server.CoreDeprecatedApiUsageStats.markedAsResolvedLastCalledAt", + "type": "string", + "tags": [], + "label": "markedAsResolvedLastCalledAt", + "description": [], + "path": "packages/core/usage-data/core-usage-data-server/src/core_usage_stats.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-usage-data-server", + "id": "def-server.CoreDeprecatedApiUsageStats.apiTotalCalls", + "type": "number", + "tags": [], + "label": "apiTotalCalls", + "description": [], + "path": "packages/core/usage-data/core-usage-data-server/src/core_usage_stats.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-usage-data-server", + "id": "def-server.CoreDeprecatedApiUsageStats.apiLastCalledAt", + "type": "string", + "tags": [], + "label": "apiLastCalledAt", + "description": [], + "path": "packages/core/usage-data/core-usage-data-server/src/core_usage_stats.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-usage-data-server", "id": "def-server.CoreEnvironmentUsageData", @@ -327,9 +396,7 @@ "parentPluginId": "@kbn/core-usage-data-server", "id": "def-server.CoreUsageDataSetup", "type": "Interface", - "tags": [ - "note" - ], + "tags": [], "label": "CoreUsageDataSetup", "description": [ "\nInternal API for registering the Usage Tracker used for Core's usage data payload.\n" @@ -385,6 +452,52 @@ } ], "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-usage-data-server", + "id": "def-server.CoreUsageDataSetup.registerDeprecatedUsageFetch", + "type": "Function", + "tags": [], + "label": "registerDeprecatedUsageFetch", + "description": [], + "signature": [ + "(fetchFn: ", + { + "pluginId": "@kbn/core-usage-data-server", + "scope": "server", + "docId": "kibKbnCoreUsageDataServerPluginApi", + "section": "def-server.DeprecatedApiUsageFetcher", + "text": "DeprecatedApiUsageFetcher" + }, + ") => void" + ], + "path": "packages/core/usage-data/core-usage-data-server/src/setup_contract.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-usage-data-server", + "id": "def-server.CoreUsageDataSetup.registerDeprecatedUsageFetch.$1", + "type": "Function", + "tags": [], + "label": "fetchFn", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-usage-data-server", + "scope": "server", + "docId": "kibKbnCoreUsageDataServerPluginApi", + "section": "def-server.DeprecatedApiUsageFetcher", + "text": "DeprecatedApiUsageFetcher" + } + ], + "path": "packages/core/usage-data/core-usage-data-server/src/setup_contract.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] } ], "initialIsOpen": false @@ -2288,6 +2401,62 @@ } ], "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-usage-data-server", + "id": "def-server.DeprecatedApiUsageFetcher", + "type": "Type", + "tags": [], + "label": "DeprecatedApiUsageFetcher", + "description": [], + "signature": [ + "(params: { soClient: ", + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-server.ISavedObjectsRepository", + "text": "ISavedObjectsRepository" + }, + "; }) => Promise<", + { + "pluginId": "@kbn/core-usage-data-server", + "scope": "server", + "docId": "kibKbnCoreUsageDataServerPluginApi", + "section": "def-server.CoreDeprecatedApiUsageStats", + "text": "CoreDeprecatedApiUsageStats" + }, + "[]>" + ], + "path": "packages/core/usage-data/core-usage-data-server/src/setup_contract.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/core-usage-data-server", + "id": "def-server.DeprecatedApiUsageFetcher.$1", + "type": "Object", + "tags": [], + "label": "params", + "description": [], + "signature": [ + "{ soClient: ", + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-server.ISavedObjectsRepository", + "text": "ISavedObjectsRepository" + }, + "; }" + ], + "path": "packages/core/usage-data/core-usage-data-server/src/setup_contract.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false } ], "objects": [] diff --git a/api_docs/kbn_core_usage_data_server.mdx b/api_docs/kbn_core_usage_data_server.mdx index 45f661ba67f11..6fe28913f2a34 100644 --- a/api_docs/kbn_core_usage_data_server.mdx +++ b/api_docs/kbn_core_usage_data_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server title: "@kbn/core-usage-data-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server'] --- import kbnCoreUsageDataServerObj from './kbn_core_usage_data_server.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 155 | 0 | 144 | 0 | +| 165 | 0 | 154 | 0 | ## Server diff --git a/api_docs/kbn_core_usage_data_server_internal.mdx b/api_docs/kbn_core_usage_data_server_internal.mdx index 32a41aef5c4dc..84d6dce63672f 100644 --- a/api_docs/kbn_core_usage_data_server_internal.mdx +++ b/api_docs/kbn_core_usage_data_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-internal title: "@kbn/core-usage-data-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-internal'] --- import kbnCoreUsageDataServerInternalObj from './kbn_core_usage_data_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_mocks.devdocs.json b/api_docs/kbn_core_usage_data_server_mocks.devdocs.json index 7ab368476bc35..d32fdca291d8c 100644 --- a/api_docs/kbn_core_usage_data_server_mocks.devdocs.json +++ b/api_docs/kbn_core_usage_data_server_mocks.devdocs.json @@ -93,7 +93,15 @@ "section": "def-server.CoreUsageStats", "text": "CoreUsageStats" }, - ">, [], unknown>; incrementSavedObjectsBulkCreate: jest.MockInstance, [options: ", + ">, [], unknown>; getDeprecatedApiUsageStats: jest.MockInstance, [], unknown>; incrementDeprecatedApi: jest.MockInstance, [counterName: string, options: { resolved?: boolean | undefined; incrementBy?: number | undefined; }], unknown>; incrementSavedObjectsBulkCreate: jest.MockInstance, [options: ", "BaseIncrementOptions", "], unknown>; incrementSavedObjectsBulkGet: jest.MockInstance, [options: ", "BaseIncrementOptions", diff --git a/api_docs/kbn_core_usage_data_server_mocks.mdx b/api_docs/kbn_core_usage_data_server_mocks.mdx index bbb2fbab016b2..844771af3608e 100644 --- a/api_docs/kbn_core_usage_data_server_mocks.mdx +++ b/api_docs/kbn_core_usage_data_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-mocks title: "@kbn/core-usage-data-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-mocks'] --- import kbnCoreUsageDataServerMocksObj from './kbn_core_usage_data_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_browser.mdx b/api_docs/kbn_core_user_profile_browser.mdx index 87fb44e559c8d..7532b55af8515 100644 --- a/api_docs/kbn_core_user_profile_browser.mdx +++ b/api_docs/kbn_core_user_profile_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser title: "@kbn/core-user-profile-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser'] --- import kbnCoreUserProfileBrowserObj from './kbn_core_user_profile_browser.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_browser_internal.mdx b/api_docs/kbn_core_user_profile_browser_internal.mdx index 8e9ce74aed16a..5316c6118fc50 100644 --- a/api_docs/kbn_core_user_profile_browser_internal.mdx +++ b/api_docs/kbn_core_user_profile_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser-internal title: "@kbn/core-user-profile-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser-internal'] --- import kbnCoreUserProfileBrowserInternalObj from './kbn_core_user_profile_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_browser_mocks.mdx b/api_docs/kbn_core_user_profile_browser_mocks.mdx index 923ea4e8d9ba6..0f11d19c24aea 100644 --- a/api_docs/kbn_core_user_profile_browser_mocks.mdx +++ b/api_docs/kbn_core_user_profile_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser-mocks title: "@kbn/core-user-profile-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser-mocks'] --- import kbnCoreUserProfileBrowserMocksObj from './kbn_core_user_profile_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_common.mdx b/api_docs/kbn_core_user_profile_common.mdx index 752b92aef933d..10836469ed162 100644 --- a/api_docs/kbn_core_user_profile_common.mdx +++ b/api_docs/kbn_core_user_profile_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-common title: "@kbn/core-user-profile-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-common plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-common'] --- import kbnCoreUserProfileCommonObj from './kbn_core_user_profile_common.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server.mdx b/api_docs/kbn_core_user_profile_server.mdx index d83e0c5972716..4ffaaff5bfe04 100644 --- a/api_docs/kbn_core_user_profile_server.mdx +++ b/api_docs/kbn_core_user_profile_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server title: "@kbn/core-user-profile-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server'] --- import kbnCoreUserProfileServerObj from './kbn_core_user_profile_server.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server_internal.mdx b/api_docs/kbn_core_user_profile_server_internal.mdx index 718ffcec07c49..5b51427cd672c 100644 --- a/api_docs/kbn_core_user_profile_server_internal.mdx +++ b/api_docs/kbn_core_user_profile_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server-internal title: "@kbn/core-user-profile-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server-internal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server-internal'] --- import kbnCoreUserProfileServerInternalObj from './kbn_core_user_profile_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server_mocks.mdx b/api_docs/kbn_core_user_profile_server_mocks.mdx index 4350e68bb2600..356afcbab8b67 100644 --- a/api_docs/kbn_core_user_profile_server_mocks.mdx +++ b/api_docs/kbn_core_user_profile_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server-mocks title: "@kbn/core-user-profile-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server-mocks'] --- import kbnCoreUserProfileServerMocksObj from './kbn_core_user_profile_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server.mdx b/api_docs/kbn_core_user_settings_server.mdx index af7431f1dbce2..76e7109acd934 100644 --- a/api_docs/kbn_core_user_settings_server.mdx +++ b/api_docs/kbn_core_user_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server title: "@kbn/core-user-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server'] --- import kbnCoreUserSettingsServerObj from './kbn_core_user_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server_mocks.mdx b/api_docs/kbn_core_user_settings_server_mocks.mdx index cf2423b68fa4f..82e7ab380a748 100644 --- a/api_docs/kbn_core_user_settings_server_mocks.mdx +++ b/api_docs/kbn_core_user_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server-mocks title: "@kbn/core-user-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server-mocks'] --- import kbnCoreUserSettingsServerMocksObj from './kbn_core_user_settings_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_crypto.mdx b/api_docs/kbn_crypto.mdx index bbf7392441178..3e0fa9bf5d4b1 100644 --- a/api_docs/kbn_crypto.mdx +++ b/api_docs/kbn_crypto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto title: "@kbn/crypto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto'] --- import kbnCryptoObj from './kbn_crypto.devdocs.json'; diff --git a/api_docs/kbn_crypto_browser.mdx b/api_docs/kbn_crypto_browser.mdx index 58d5bba50e599..b83fcf61f044c 100644 --- a/api_docs/kbn_crypto_browser.mdx +++ b/api_docs/kbn_crypto_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto-browser title: "@kbn/crypto-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto-browser plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_custom_icons.mdx b/api_docs/kbn_custom_icons.mdx index 7c24f573d9ecb..9f663407eaf8c 100644 --- a/api_docs/kbn_custom_icons.mdx +++ b/api_docs/kbn_custom_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-custom-icons title: "@kbn/custom-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/custom-icons plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/custom-icons'] --- import kbnCustomIconsObj from './kbn_custom_icons.devdocs.json'; diff --git a/api_docs/kbn_custom_integrations.mdx b/api_docs/kbn_custom_integrations.mdx index a25365a929724..ae0f788539315 100644 --- a/api_docs/kbn_custom_integrations.mdx +++ b/api_docs/kbn_custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-custom-integrations title: "@kbn/custom-integrations" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/custom-integrations plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/custom-integrations'] --- import kbnCustomIntegrationsObj from './kbn_custom_integrations.devdocs.json'; diff --git a/api_docs/kbn_cypress_config.mdx b/api_docs/kbn_cypress_config.mdx index 7cd3341afee33..9a00fe84f8863 100644 --- a/api_docs/kbn_cypress_config.mdx +++ b/api_docs/kbn_cypress_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cypress-config title: "@kbn/cypress-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cypress-config plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cypress-config'] --- import kbnCypressConfigObj from './kbn_cypress_config.devdocs.json'; diff --git a/api_docs/kbn_data_forge.mdx b/api_docs/kbn_data_forge.mdx index 7a3cf86e7852c..2629ad96f3114 100644 --- a/api_docs/kbn_data_forge.mdx +++ b/api_docs/kbn_data_forge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-forge title: "@kbn/data-forge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-forge plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-forge'] --- import kbnDataForgeObj from './kbn_data_forge.devdocs.json'; diff --git a/api_docs/kbn_data_service.mdx b/api_docs/kbn_data_service.mdx index d595eb2ed2cd4..d18c2f5ac0c02 100644 --- a/api_docs/kbn_data_service.mdx +++ b/api_docs/kbn_data_service.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-service title: "@kbn/data-service" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-service plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-service'] --- import kbnDataServiceObj from './kbn_data_service.devdocs.json'; diff --git a/api_docs/kbn_data_stream_adapter.mdx b/api_docs/kbn_data_stream_adapter.mdx index 8f39ba44e8be1..5613bf12b696b 100644 --- a/api_docs/kbn_data_stream_adapter.mdx +++ b/api_docs/kbn_data_stream_adapter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-stream-adapter title: "@kbn/data-stream-adapter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-stream-adapter plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-stream-adapter'] --- import kbnDataStreamAdapterObj from './kbn_data_stream_adapter.devdocs.json'; diff --git a/api_docs/kbn_data_view_utils.mdx b/api_docs/kbn_data_view_utils.mdx index 4b2b716b07aa1..70668ec224d39 100644 --- a/api_docs/kbn_data_view_utils.mdx +++ b/api_docs/kbn_data_view_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-view-utils title: "@kbn/data-view-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-view-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-view-utils'] --- import kbnDataViewUtilsObj from './kbn_data_view_utils.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index 86d7c23b471e9..3ab5b5295c3b8 100644 --- a/api_docs/kbn_datemath.mdx +++ b/api_docs/kbn_datemath.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-datemath title: "@kbn/datemath" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/datemath plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] --- import kbnDatemathObj from './kbn_datemath.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_analytics.mdx b/api_docs/kbn_deeplinks_analytics.mdx index 474b162d50a14..ff663a097042e 100644 --- a/api_docs/kbn_deeplinks_analytics.mdx +++ b/api_docs/kbn_deeplinks_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-analytics title: "@kbn/deeplinks-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-analytics plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-analytics'] --- import kbnDeeplinksAnalyticsObj from './kbn_deeplinks_analytics.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_devtools.mdx b/api_docs/kbn_deeplinks_devtools.mdx index 6bee94f81153a..7f3e6ef8d4f5e 100644 --- a/api_docs/kbn_deeplinks_devtools.mdx +++ b/api_docs/kbn_deeplinks_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-devtools title: "@kbn/deeplinks-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-devtools plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-devtools'] --- import kbnDeeplinksDevtoolsObj from './kbn_deeplinks_devtools.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_fleet.mdx b/api_docs/kbn_deeplinks_fleet.mdx index 88053579a0bcb..1bbb51fdc3d1d 100644 --- a/api_docs/kbn_deeplinks_fleet.mdx +++ b/api_docs/kbn_deeplinks_fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-fleet title: "@kbn/deeplinks-fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-fleet plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-fleet'] --- import kbnDeeplinksFleetObj from './kbn_deeplinks_fleet.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_management.mdx b/api_docs/kbn_deeplinks_management.mdx index 1d7e3036d9bf8..885502c27a406 100644 --- a/api_docs/kbn_deeplinks_management.mdx +++ b/api_docs/kbn_deeplinks_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-management title: "@kbn/deeplinks-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-management plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-management'] --- import kbnDeeplinksManagementObj from './kbn_deeplinks_management.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_ml.mdx b/api_docs/kbn_deeplinks_ml.mdx index 458703dbb50e0..e3da77c2c9c79 100644 --- a/api_docs/kbn_deeplinks_ml.mdx +++ b/api_docs/kbn_deeplinks_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-ml title: "@kbn/deeplinks-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-ml plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-ml'] --- import kbnDeeplinksMlObj from './kbn_deeplinks_ml.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_observability.mdx b/api_docs/kbn_deeplinks_observability.mdx index c1f6c5532be28..de08fc3ec294a 100644 --- a/api_docs/kbn_deeplinks_observability.mdx +++ b/api_docs/kbn_deeplinks_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-observability title: "@kbn/deeplinks-observability" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-observability plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-observability'] --- import kbnDeeplinksObservabilityObj from './kbn_deeplinks_observability.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_search.mdx b/api_docs/kbn_deeplinks_search.mdx index 9ae979c247625..2b29b0d3017e2 100644 --- a/api_docs/kbn_deeplinks_search.mdx +++ b/api_docs/kbn_deeplinks_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-search title: "@kbn/deeplinks-search" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-search plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-search'] --- import kbnDeeplinksSearchObj from './kbn_deeplinks_search.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_security.mdx b/api_docs/kbn_deeplinks_security.mdx index 53af997750cc1..ee710ec947f74 100644 --- a/api_docs/kbn_deeplinks_security.mdx +++ b/api_docs/kbn_deeplinks_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-security title: "@kbn/deeplinks-security" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-security plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-security'] --- import kbnDeeplinksSecurityObj from './kbn_deeplinks_security.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_shared.mdx b/api_docs/kbn_deeplinks_shared.mdx index 60e4eaf334c03..d6815fac16393 100644 --- a/api_docs/kbn_deeplinks_shared.mdx +++ b/api_docs/kbn_deeplinks_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-shared title: "@kbn/deeplinks-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-shared plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-shared'] --- import kbnDeeplinksSharedObj from './kbn_deeplinks_shared.devdocs.json'; diff --git a/api_docs/kbn_default_nav_analytics.mdx b/api_docs/kbn_default_nav_analytics.mdx index 7de3c06600d90..f41051dc93d52 100644 --- a/api_docs/kbn_default_nav_analytics.mdx +++ b/api_docs/kbn_default_nav_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-analytics title: "@kbn/default-nav-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-analytics plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-analytics'] --- import kbnDefaultNavAnalyticsObj from './kbn_default_nav_analytics.devdocs.json'; diff --git a/api_docs/kbn_default_nav_devtools.mdx b/api_docs/kbn_default_nav_devtools.mdx index d1212f25a3b8a..b20f5bb5811b9 100644 --- a/api_docs/kbn_default_nav_devtools.mdx +++ b/api_docs/kbn_default_nav_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-devtools title: "@kbn/default-nav-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-devtools plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-devtools'] --- import kbnDefaultNavDevtoolsObj from './kbn_default_nav_devtools.devdocs.json'; diff --git a/api_docs/kbn_default_nav_management.mdx b/api_docs/kbn_default_nav_management.mdx index 4b43300ab229d..6b2b6e027e614 100644 --- a/api_docs/kbn_default_nav_management.mdx +++ b/api_docs/kbn_default_nav_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-management title: "@kbn/default-nav-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-management plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-management'] --- import kbnDefaultNavManagementObj from './kbn_default_nav_management.devdocs.json'; diff --git a/api_docs/kbn_default_nav_ml.mdx b/api_docs/kbn_default_nav_ml.mdx index eb5e25e02a433..f2046def2cd30 100644 --- a/api_docs/kbn_default_nav_ml.mdx +++ b/api_docs/kbn_default_nav_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-ml title: "@kbn/default-nav-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-ml plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-ml'] --- import kbnDefaultNavMlObj from './kbn_default_nav_ml.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index 7267ab51d0427..7603bd12a3236 100644 --- a/api_docs/kbn_dev_cli_errors.mdx +++ b/api_docs/kbn_dev_cli_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-errors title: "@kbn/dev-cli-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-errors plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-errors'] --- import kbnDevCliErrorsObj from './kbn_dev_cli_errors.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_runner.mdx b/api_docs/kbn_dev_cli_runner.mdx index f559084d644b4..0d838f3ff43b5 100644 --- a/api_docs/kbn_dev_cli_runner.mdx +++ b/api_docs/kbn_dev_cli_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-runner title: "@kbn/dev-cli-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-runner plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-runner'] --- import kbnDevCliRunnerObj from './kbn_dev_cli_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_proc_runner.mdx b/api_docs/kbn_dev_proc_runner.mdx index 7bc96b8af35d3..b7ad5b49e3b56 100644 --- a/api_docs/kbn_dev_proc_runner.mdx +++ b/api_docs/kbn_dev_proc_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-proc-runner title: "@kbn/dev-proc-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-proc-runner plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-proc-runner'] --- import kbnDevProcRunnerObj from './kbn_dev_proc_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_utils.mdx b/api_docs/kbn_dev_utils.mdx index 25cb35f978158..0f707300ec763 100644 --- a/api_docs/kbn_dev_utils.mdx +++ b/api_docs/kbn_dev_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-utils title: "@kbn/dev-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_discover_utils.mdx b/api_docs/kbn_discover_utils.mdx index a2c9f2ed4fb5b..0836f9212736f 100644 --- a/api_docs/kbn_discover_utils.mdx +++ b/api_docs/kbn_discover_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-discover-utils title: "@kbn/discover-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/discover-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/discover-utils'] --- import kbnDiscoverUtilsObj from './kbn_discover_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index bf2107e779d36..35a6d45a72177 100644 --- a/api_docs/kbn_doc_links.mdx +++ b/api_docs/kbn_doc_links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-doc-links title: "@kbn/doc-links" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/doc-links plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/doc-links'] --- import kbnDocLinksObj from './kbn_doc_links.devdocs.json'; diff --git a/api_docs/kbn_docs_utils.mdx b/api_docs/kbn_docs_utils.mdx index bf01c7941c012..9c8d117d82cd9 100644 --- a/api_docs/kbn_docs_utils.mdx +++ b/api_docs/kbn_docs_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-docs-utils title: "@kbn/docs-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/docs-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] --- import kbnDocsUtilsObj from './kbn_docs_utils.devdocs.json'; diff --git a/api_docs/kbn_dom_drag_drop.mdx b/api_docs/kbn_dom_drag_drop.mdx index 2b6bdd33ced4d..571324a4e88b8 100644 --- a/api_docs/kbn_dom_drag_drop.mdx +++ b/api_docs/kbn_dom_drag_drop.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dom-drag-drop title: "@kbn/dom-drag-drop" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dom-drag-drop plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dom-drag-drop'] --- import kbnDomDragDropObj from './kbn_dom_drag_drop.devdocs.json'; diff --git a/api_docs/kbn_ebt_tools.mdx b/api_docs/kbn_ebt_tools.mdx index 68a94a0864393..e7d20bbf6b793 100644 --- a/api_docs/kbn_ebt_tools.mdx +++ b/api_docs/kbn_ebt_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ebt-tools title: "@kbn/ebt-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ebt-tools plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_ecs_data_quality_dashboard.mdx b/api_docs/kbn_ecs_data_quality_dashboard.mdx index 9f5360fbc4218..3ad3b42350c3a 100644 --- a/api_docs/kbn_ecs_data_quality_dashboard.mdx +++ b/api_docs/kbn_ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs-data-quality-dashboard title: "@kbn/ecs-data-quality-dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs-data-quality-dashboard plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs-data-quality-dashboard'] --- import kbnEcsDataQualityDashboardObj from './kbn_ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/kbn_elastic_agent_utils.mdx b/api_docs/kbn_elastic_agent_utils.mdx index a8ed47c74f76a..eaee0a7b617a9 100644 --- a/api_docs/kbn_elastic_agent_utils.mdx +++ b/api_docs/kbn_elastic_agent_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-agent-utils title: "@kbn/elastic-agent-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-agent-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-agent-utils'] --- import kbnElasticAgentUtilsObj from './kbn_elastic_agent_utils.devdocs.json'; diff --git a/api_docs/kbn_elastic_assistant.mdx b/api_docs/kbn_elastic_assistant.mdx index f263f6f93bfb4..f75c56f0c3c5d 100644 --- a/api_docs/kbn_elastic_assistant.mdx +++ b/api_docs/kbn_elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-assistant title: "@kbn/elastic-assistant" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-assistant plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant'] --- import kbnElasticAssistantObj from './kbn_elastic_assistant.devdocs.json'; diff --git a/api_docs/kbn_elastic_assistant_common.mdx b/api_docs/kbn_elastic_assistant_common.mdx index 80c1bf6052af6..a237509f05591 100644 --- a/api_docs/kbn_elastic_assistant_common.mdx +++ b/api_docs/kbn_elastic_assistant_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-assistant-common title: "@kbn/elastic-assistant-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-assistant-common plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant-common'] --- import kbnElasticAssistantCommonObj from './kbn_elastic_assistant_common.devdocs.json'; diff --git a/api_docs/kbn_entities_schema.mdx b/api_docs/kbn_entities_schema.mdx index 48dfd09e084f6..30d5beaa5515f 100644 --- a/api_docs/kbn_entities_schema.mdx +++ b/api_docs/kbn_entities_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-entities-schema title: "@kbn/entities-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/entities-schema plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/entities-schema'] --- import kbnEntitiesSchemaObj from './kbn_entities_schema.devdocs.json'; diff --git a/api_docs/kbn_es.mdx b/api_docs/kbn_es.mdx index aac9a97cfe181..eb65f6efb5d6d 100644 --- a/api_docs/kbn_es.mdx +++ b/api_docs/kbn_es.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es title: "@kbn/es" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es'] --- import kbnEsObj from './kbn_es.devdocs.json'; diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx index 6c967b70ceb51..fa37d599b6279 100644 --- a/api_docs/kbn_es_archiver.mdx +++ b/api_docs/kbn_es_archiver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-archiver title: "@kbn/es-archiver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-archiver plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-archiver'] --- import kbnEsArchiverObj from './kbn_es_archiver.devdocs.json'; diff --git a/api_docs/kbn_es_errors.mdx b/api_docs/kbn_es_errors.mdx index 44dc96f1d90f0..79c39564ec545 100644 --- a/api_docs/kbn_es_errors.mdx +++ b/api_docs/kbn_es_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-errors title: "@kbn/es-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-errors plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-errors'] --- import kbnEsErrorsObj from './kbn_es_errors.devdocs.json'; diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index 0f65b28c397e8..a32af69363664 100644 --- a/api_docs/kbn_es_query.mdx +++ b/api_docs/kbn_es_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-query title: "@kbn/es-query" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-query plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-query'] --- import kbnEsQueryObj from './kbn_es_query.devdocs.json'; diff --git a/api_docs/kbn_es_types.mdx b/api_docs/kbn_es_types.mdx index 7dd6434278d39..284a48cc3c5db 100644 --- a/api_docs/kbn_es_types.mdx +++ b/api_docs/kbn_es_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-types title: "@kbn/es-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-types plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-types'] --- import kbnEsTypesObj from './kbn_es_types.devdocs.json'; diff --git a/api_docs/kbn_eslint_plugin_imports.mdx b/api_docs/kbn_eslint_plugin_imports.mdx index 323def425c0f9..36c728a36f14d 100644 --- a/api_docs/kbn_eslint_plugin_imports.mdx +++ b/api_docs/kbn_eslint_plugin_imports.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-eslint-plugin-imports title: "@kbn/eslint-plugin-imports" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/eslint-plugin-imports plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_esql_ast.devdocs.json b/api_docs/kbn_esql_ast.devdocs.json index 5d8e934eb8994..cac01fac9555d 100644 --- a/api_docs/kbn_esql_ast.devdocs.json +++ b/api_docs/kbn_esql_ast.devdocs.json @@ -487,6 +487,67 @@ ], "returnComment": [] }, + { + "parentPluginId": "@kbn/esql-ast", + "id": "def-common.BasicPrettyPrinter.simplifyMultiplicationByOne", + "type": "Function", + "tags": [], + "label": "simplifyMultiplicationByOne", + "description": [], + "signature": [ + "(node: ", + { + "pluginId": "@kbn/esql-ast", + "scope": "common", + "docId": "kibKbnEsqlAstPluginApi", + "section": "def-common.ESQLSingleAstItem", + "text": "ESQLSingleAstItem" + }, + ", minusCount?: number) => string | undefined" + ], + "path": "packages/kbn-esql-ast/src/pretty_print/basic_pretty_printer.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/esql-ast", + "id": "def-common.BasicPrettyPrinter.simplifyMultiplicationByOne.$1", + "type": "CompoundType", + "tags": [], + "label": "node", + "description": [], + "signature": [ + { + "pluginId": "@kbn/esql-ast", + "scope": "common", + "docId": "kibKbnEsqlAstPluginApi", + "section": "def-common.ESQLSingleAstItem", + "text": "ESQLSingleAstItem" + } + ], + "path": "packages/kbn-esql-ast/src/pretty_print/basic_pretty_printer.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/esql-ast", + "id": "def-common.BasicPrettyPrinter.simplifyMultiplicationByOne.$2", + "type": "number", + "tags": [], + "label": "minusCount", + "description": [], + "signature": [ + "number" + ], + "path": "packages/kbn-esql-ast/src/pretty_print/basic_pretty_printer.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, { "parentPluginId": "@kbn/esql-ast", "id": "def-common.BasicPrettyPrinter.visitor", diff --git a/api_docs/kbn_esql_ast.mdx b/api_docs/kbn_esql_ast.mdx index fd6b8cf069f94..343cf5cea540f 100644 --- a/api_docs/kbn_esql_ast.mdx +++ b/api_docs/kbn_esql_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-ast title: "@kbn/esql-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-ast plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-ast'] --- import kbnEsqlAstObj from './kbn_esql_ast.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 266 | 1 | 208 | 34 | +| 269 | 1 | 211 | 34 | ## Common diff --git a/api_docs/kbn_esql_editor.mdx b/api_docs/kbn_esql_editor.mdx index 8853e8f93e4d9..ed71a80ed11bd 100644 --- a/api_docs/kbn_esql_editor.mdx +++ b/api_docs/kbn_esql_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-editor title: "@kbn/esql-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-editor plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-editor'] --- import kbnEsqlEditorObj from './kbn_esql_editor.devdocs.json'; diff --git a/api_docs/kbn_esql_utils.mdx b/api_docs/kbn_esql_utils.mdx index cd34738b4b00c..15aeb10b64833 100644 --- a/api_docs/kbn_esql_utils.mdx +++ b/api_docs/kbn_esql_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-utils title: "@kbn/esql-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-utils'] --- import kbnEsqlUtilsObj from './kbn_esql_utils.devdocs.json'; diff --git a/api_docs/kbn_esql_validation_autocomplete.mdx b/api_docs/kbn_esql_validation_autocomplete.mdx index 30d5f1c89ca98..ebd0f86e6a87f 100644 --- a/api_docs/kbn_esql_validation_autocomplete.mdx +++ b/api_docs/kbn_esql_validation_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-validation-autocomplete title: "@kbn/esql-validation-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-validation-autocomplete plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-validation-autocomplete'] --- import kbnEsqlValidationAutocompleteObj from './kbn_esql_validation_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_event_annotation_common.mdx b/api_docs/kbn_event_annotation_common.mdx index 709419a5cbc41..c08ad2d303880 100644 --- a/api_docs/kbn_event_annotation_common.mdx +++ b/api_docs/kbn_event_annotation_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-common title: "@kbn/event-annotation-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-common plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/event-annotation-common'] --- import kbnEventAnnotationCommonObj from './kbn_event_annotation_common.devdocs.json'; diff --git a/api_docs/kbn_event_annotation_components.mdx b/api_docs/kbn_event_annotation_components.mdx index ca3b6adbd80b0..c98ab9de56aff 100644 --- a/api_docs/kbn_event_annotation_components.mdx +++ b/api_docs/kbn_event_annotation_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-components title: "@kbn/event-annotation-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-components plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/event-annotation-components'] --- import kbnEventAnnotationComponentsObj from './kbn_event_annotation_components.devdocs.json'; diff --git a/api_docs/kbn_expandable_flyout.mdx b/api_docs/kbn_expandable_flyout.mdx index 9b1f70fc890a4..a34ac2b1772e5 100644 --- a/api_docs/kbn_expandable_flyout.mdx +++ b/api_docs/kbn_expandable_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-expandable-flyout title: "@kbn/expandable-flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/expandable-flyout plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/expandable-flyout'] --- import kbnExpandableFlyoutObj from './kbn_expandable_flyout.devdocs.json'; diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index 064d30fc56972..d655980b6198d 100644 --- a/api_docs/kbn_field_types.mdx +++ b/api_docs/kbn_field_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-types title: "@kbn/field-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-types plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] --- import kbnFieldTypesObj from './kbn_field_types.devdocs.json'; diff --git a/api_docs/kbn_field_utils.mdx b/api_docs/kbn_field_utils.mdx index 02abb853ab8a5..21d346979b6e0 100644 --- a/api_docs/kbn_field_utils.mdx +++ b/api_docs/kbn_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-utils title: "@kbn/field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-utils'] --- import kbnFieldUtilsObj from './kbn_field_utils.devdocs.json'; diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index 0ce209f2cbbde..3729b6b529726 100644 --- a/api_docs/kbn_find_used_node_modules.mdx +++ b/api_docs/kbn_find_used_node_modules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-find-used-node-modules title: "@kbn/find-used-node-modules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/find-used-node-modules plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/find-used-node-modules'] --- import kbnFindUsedNodeModulesObj from './kbn_find_used_node_modules.devdocs.json'; diff --git a/api_docs/kbn_formatters.mdx b/api_docs/kbn_formatters.mdx index 80bd4af28772d..9c26fad7579df 100644 --- a/api_docs/kbn_formatters.mdx +++ b/api_docs/kbn_formatters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-formatters title: "@kbn/formatters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/formatters plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/formatters'] --- import kbnFormattersObj from './kbn_formatters.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index a6088a21adaf3..5d5edbb421415 100644 --- a/api_docs/kbn_ftr_common_functional_services.mdx +++ b/api_docs/kbn_ftr_common_functional_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-services title: "@kbn/ftr-common-functional-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-services plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-services'] --- import kbnFtrCommonFunctionalServicesObj from './kbn_ftr_common_functional_services.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_ui_services.mdx b/api_docs/kbn_ftr_common_functional_ui_services.mdx index 18b423b8c6f25..d746dcf6c1ee7 100644 --- a/api_docs/kbn_ftr_common_functional_ui_services.mdx +++ b/api_docs/kbn_ftr_common_functional_ui_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-ui-services title: "@kbn/ftr-common-functional-ui-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-ui-services plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-ui-services'] --- import kbnFtrCommonFunctionalUiServicesObj from './kbn_ftr_common_functional_ui_services.devdocs.json'; diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx index ad86426cab2c0..88c5da95797a1 100644 --- a/api_docs/kbn_generate.mdx +++ b/api_docs/kbn_generate.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate title: "@kbn/generate" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_generate_console_definitions.mdx b/api_docs/kbn_generate_console_definitions.mdx index 1c1026e6033a3..8c102e5ffb44e 100644 --- a/api_docs/kbn_generate_console_definitions.mdx +++ b/api_docs/kbn_generate_console_definitions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-console-definitions title: "@kbn/generate-console-definitions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-console-definitions plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-console-definitions'] --- import kbnGenerateConsoleDefinitionsObj from './kbn_generate_console_definitions.devdocs.json'; diff --git a/api_docs/kbn_generate_csv.mdx b/api_docs/kbn_generate_csv.mdx index eb44ee7e1d1f3..55b63c26c7883 100644 --- a/api_docs/kbn_generate_csv.mdx +++ b/api_docs/kbn_generate_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-csv title: "@kbn/generate-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-csv plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv'] --- import kbnGenerateCsvObj from './kbn_generate_csv.devdocs.json'; diff --git a/api_docs/kbn_grid_layout.mdx b/api_docs/kbn_grid_layout.mdx index 7b27c0ff49662..d08510cfe9146 100644 --- a/api_docs/kbn_grid_layout.mdx +++ b/api_docs/kbn_grid_layout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-grid-layout title: "@kbn/grid-layout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/grid-layout plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/grid-layout'] --- import kbnGridLayoutObj from './kbn_grid_layout.devdocs.json'; diff --git a/api_docs/kbn_grouping.mdx b/api_docs/kbn_grouping.mdx index 025c6254ec165..287f035d02383 100644 --- a/api_docs/kbn_grouping.mdx +++ b/api_docs/kbn_grouping.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-grouping title: "@kbn/grouping" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/grouping plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/grouping'] --- import kbnGroupingObj from './kbn_grouping.devdocs.json'; diff --git a/api_docs/kbn_guided_onboarding.mdx b/api_docs/kbn_guided_onboarding.mdx index 302a7c1ebc8cf..769ad16a2f40e 100644 --- a/api_docs/kbn_guided_onboarding.mdx +++ b/api_docs/kbn_guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-guided-onboarding title: "@kbn/guided-onboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/guided-onboarding plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/guided-onboarding'] --- import kbnGuidedOnboardingObj from './kbn_guided_onboarding.devdocs.json'; diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index c9874e40289eb..44445cbae253d 100644 --- a/api_docs/kbn_handlebars.mdx +++ b/api_docs/kbn_handlebars.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-handlebars title: "@kbn/handlebars" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/handlebars plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/handlebars'] --- import kbnHandlebarsObj from './kbn_handlebars.devdocs.json'; diff --git a/api_docs/kbn_hapi_mocks.mdx b/api_docs/kbn_hapi_mocks.mdx index 066593be58f7d..44e84764ace7d 100644 --- a/api_docs/kbn_hapi_mocks.mdx +++ b/api_docs/kbn_hapi_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-hapi-mocks title: "@kbn/hapi-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/hapi-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/hapi-mocks'] --- import kbnHapiMocksObj from './kbn_hapi_mocks.devdocs.json'; diff --git a/api_docs/kbn_health_gateway_server.mdx b/api_docs/kbn_health_gateway_server.mdx index 38f184fd56e6a..40b0d1689b6d7 100644 --- a/api_docs/kbn_health_gateway_server.mdx +++ b/api_docs/kbn_health_gateway_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-health-gateway-server title: "@kbn/health-gateway-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/health-gateway-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/health-gateway-server'] --- import kbnHealthGatewayServerObj from './kbn_health_gateway_server.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_card.mdx b/api_docs/kbn_home_sample_data_card.mdx index 06f895a796e0a..a5bd00eb22f81 100644 --- a/api_docs/kbn_home_sample_data_card.mdx +++ b/api_docs/kbn_home_sample_data_card.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-card title: "@kbn/home-sample-data-card" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-card plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-card'] --- import kbnHomeSampleDataCardObj from './kbn_home_sample_data_card.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_tab.mdx b/api_docs/kbn_home_sample_data_tab.mdx index 6139095d66f74..5264ef503fc2d 100644 --- a/api_docs/kbn_home_sample_data_tab.mdx +++ b/api_docs/kbn_home_sample_data_tab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-tab title: "@kbn/home-sample-data-tab" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-tab plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-tab'] --- import kbnHomeSampleDataTabObj from './kbn_home_sample_data_tab.devdocs.json'; diff --git a/api_docs/kbn_i18n.mdx b/api_docs/kbn_i18n.mdx index 6e0bd7671e660..86a6311d2a306 100644 --- a/api_docs/kbn_i18n.mdx +++ b/api_docs/kbn_i18n.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n title: "@kbn/i18n" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n'] --- import kbnI18nObj from './kbn_i18n.devdocs.json'; diff --git a/api_docs/kbn_i18n_react.mdx b/api_docs/kbn_i18n_react.mdx index c6be1004c1740..659eba74ea166 100644 --- a/api_docs/kbn_i18n_react.mdx +++ b/api_docs/kbn_i18n_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n-react title: "@kbn/i18n-react" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n-react plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n-react'] --- import kbnI18nReactObj from './kbn_i18n_react.devdocs.json'; diff --git a/api_docs/kbn_import_resolver.mdx b/api_docs/kbn_import_resolver.mdx index b0be64184db3f..6fd53de34047d 100644 --- a/api_docs/kbn_import_resolver.mdx +++ b/api_docs/kbn_import_resolver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-import-resolver title: "@kbn/import-resolver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/import-resolver plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_index_management_shared_types.mdx b/api_docs/kbn_index_management_shared_types.mdx index 96b66f7b069eb..a2574f6b29860 100644 --- a/api_docs/kbn_index_management_shared_types.mdx +++ b/api_docs/kbn_index_management_shared_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-index-management-shared-types title: "@kbn/index-management-shared-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/index-management-shared-types plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/index-management-shared-types'] --- import kbnIndexManagementSharedTypesObj from './kbn_index_management_shared_types.devdocs.json'; diff --git a/api_docs/kbn_inference_integration_flyout.mdx b/api_docs/kbn_inference_integration_flyout.mdx index 7297345237eb1..14e63a7ca5251 100644 --- a/api_docs/kbn_inference_integration_flyout.mdx +++ b/api_docs/kbn_inference_integration_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-inference_integration_flyout title: "@kbn/inference_integration_flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/inference_integration_flyout plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/inference_integration_flyout'] --- import kbnInferenceIntegrationFlyoutObj from './kbn_inference_integration_flyout.devdocs.json'; diff --git a/api_docs/kbn_infra_forge.mdx b/api_docs/kbn_infra_forge.mdx index 0729b097113f2..08ffb112321b7 100644 --- a/api_docs/kbn_infra_forge.mdx +++ b/api_docs/kbn_infra_forge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-infra-forge title: "@kbn/infra-forge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/infra-forge plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/infra-forge'] --- import kbnInfraForgeObj from './kbn_infra_forge.devdocs.json'; diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index 5510ef3ea6b2f..e1119f9b6d3b1 100644 --- a/api_docs/kbn_interpreter.mdx +++ b/api_docs/kbn_interpreter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-interpreter title: "@kbn/interpreter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/interpreter plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter'] --- import kbnInterpreterObj from './kbn_interpreter.devdocs.json'; diff --git a/api_docs/kbn_investigation_shared.mdx b/api_docs/kbn_investigation_shared.mdx index ac83c5282611b..64fa1d7454eb9 100644 --- a/api_docs/kbn_investigation_shared.mdx +++ b/api_docs/kbn_investigation_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-investigation-shared title: "@kbn/investigation-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/investigation-shared plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/investigation-shared'] --- import kbnInvestigationSharedObj from './kbn_investigation_shared.devdocs.json'; diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx index 3a4841f1b89bb..61b2ee4fb0c48 100644 --- a/api_docs/kbn_io_ts_utils.mdx +++ b/api_docs/kbn_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-io-ts-utils title: "@kbn/io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/io-ts-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_ipynb.mdx b/api_docs/kbn_ipynb.mdx index 6bcf8efa91024..be01616222844 100644 --- a/api_docs/kbn_ipynb.mdx +++ b/api_docs/kbn_ipynb.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ipynb title: "@kbn/ipynb" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ipynb plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ipynb'] --- import kbnIpynbObj from './kbn_ipynb.devdocs.json'; diff --git a/api_docs/kbn_item_buffer.mdx b/api_docs/kbn_item_buffer.mdx index c58f2bc382d34..6eca637c561b2 100644 --- a/api_docs/kbn_item_buffer.mdx +++ b/api_docs/kbn_item_buffer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-item-buffer title: "@kbn/item-buffer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/item-buffer plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/item-buffer'] --- import kbnItemBufferObj from './kbn_item_buffer.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index 21b1c89eef8df..25a2c7bf39fef 100644 --- a/api_docs/kbn_jest_serializers.mdx +++ b/api_docs/kbn_jest_serializers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-jest-serializers title: "@kbn/jest-serializers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/jest-serializers plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/jest-serializers'] --- import kbnJestSerializersObj from './kbn_jest_serializers.devdocs.json'; diff --git a/api_docs/kbn_journeys.mdx b/api_docs/kbn_journeys.mdx index 648e0c6129c14..b384fb4fd04be 100644 --- a/api_docs/kbn_journeys.mdx +++ b/api_docs/kbn_journeys.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-journeys title: "@kbn/journeys" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/journeys plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/journeys'] --- import kbnJourneysObj from './kbn_journeys.devdocs.json'; diff --git a/api_docs/kbn_json_ast.mdx b/api_docs/kbn_json_ast.mdx index 8dfbe88d21808..e636f9eda15b7 100644 --- a/api_docs/kbn_json_ast.mdx +++ b/api_docs/kbn_json_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-json-ast title: "@kbn/json-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/json-ast plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/json-ast'] --- import kbnJsonAstObj from './kbn_json_ast.devdocs.json'; diff --git a/api_docs/kbn_json_schemas.mdx b/api_docs/kbn_json_schemas.mdx index eb94a737c8c60..6ab15f200e8cd 100644 --- a/api_docs/kbn_json_schemas.mdx +++ b/api_docs/kbn_json_schemas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-json-schemas title: "@kbn/json-schemas" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/json-schemas plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/json-schemas'] --- import kbnJsonSchemasObj from './kbn_json_schemas.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_schema.devdocs.json b/api_docs/kbn_kibana_manifest_schema.devdocs.json index e9509c172d6e8..296e6527f7bcb 100644 --- a/api_docs/kbn_kibana_manifest_schema.devdocs.json +++ b/api_docs/kbn_kibana_manifest_schema.devdocs.json @@ -989,6 +989,104 @@ } ] }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.group", + "type": "Object", + "tags": [], + "label": "group", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.group.enum", + "type": "Array", + "tags": [], + "label": "enum", + "description": [], + "signature": [ + "string[]" + ], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.group.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.group.default", + "type": "string", + "tags": [], + "label": "default", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.visibility", + "type": "Object", + "tags": [], + "label": "visibility", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.visibility.enum", + "type": "Array", + "tags": [], + "label": "enum", + "description": [], + "signature": [ + "string[]" + ], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.visibility.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/kibana-manifest-schema", + "id": "def-common.MANIFEST_V2.properties.visibility.default", + "type": "string", + "tags": [], + "label": "default", + "description": [], + "path": "packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, { "parentPluginId": "@kbn/kibana-manifest-schema", "id": "def-common.MANIFEST_V2.properties.devOnly", diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index b4046be8a9342..23c82f22047ce 100644 --- a/api_docs/kbn_kibana_manifest_schema.mdx +++ b/api_docs/kbn_kibana_manifest_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-kibana-manifest-schema title: "@kbn/kibana-manifest-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/kibana-manifest-schema plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-schema'] --- import kbnKibanaManifestSchemaObj from './kbn_kibana_manifest_schema.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kiban | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 108 | 0 | 107 | 0 | +| 116 | 0 | 115 | 0 | ## Common diff --git a/api_docs/kbn_language_documentation.mdx b/api_docs/kbn_language_documentation.mdx index 967010b714839..30ac4f4c4700b 100644 --- a/api_docs/kbn_language_documentation.mdx +++ b/api_docs/kbn_language_documentation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-language-documentation title: "@kbn/language-documentation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/language-documentation plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/language-documentation'] --- import kbnLanguageDocumentationObj from './kbn_language_documentation.devdocs.json'; diff --git a/api_docs/kbn_lens_embeddable_utils.mdx b/api_docs/kbn_lens_embeddable_utils.mdx index f0fefe25521ac..482caa2c1b967 100644 --- a/api_docs/kbn_lens_embeddable_utils.mdx +++ b/api_docs/kbn_lens_embeddable_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-lens-embeddable-utils title: "@kbn/lens-embeddable-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/lens-embeddable-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/lens-embeddable-utils'] --- import kbnLensEmbeddableUtilsObj from './kbn_lens_embeddable_utils.devdocs.json'; diff --git a/api_docs/kbn_lens_formula_docs.mdx b/api_docs/kbn_lens_formula_docs.mdx index 68dbfb7c07e71..ca6a531bde925 100644 --- a/api_docs/kbn_lens_formula_docs.mdx +++ b/api_docs/kbn_lens_formula_docs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-lens-formula-docs title: "@kbn/lens-formula-docs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/lens-formula-docs plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/lens-formula-docs'] --- import kbnLensFormulaDocsObj from './kbn_lens_formula_docs.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index 53e7b9000ce7e..92f6f33ce7b49 100644 --- a/api_docs/kbn_logging.mdx +++ b/api_docs/kbn_logging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging title: "@kbn/logging" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging'] --- import kbnLoggingObj from './kbn_logging.devdocs.json'; diff --git a/api_docs/kbn_logging_mocks.mdx b/api_docs/kbn_logging_mocks.mdx index e0e377d73e538..31c6a4200476f 100644 --- a/api_docs/kbn_logging_mocks.mdx +++ b/api_docs/kbn_logging_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging-mocks title: "@kbn/logging-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_content_badge.mdx b/api_docs/kbn_managed_content_badge.mdx index 375e307bbdc8f..a84bd5b5adf38 100644 --- a/api_docs/kbn_managed_content_badge.mdx +++ b/api_docs/kbn_managed_content_badge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-content-badge title: "@kbn/managed-content-badge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-content-badge plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-content-badge'] --- import kbnManagedContentBadgeObj from './kbn_managed_content_badge.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index e2838fb14cabb..daa0edba16b4f 100644 --- a/api_docs/kbn_managed_vscode_config.mdx +++ b/api_docs/kbn_managed_vscode_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-vscode-config title: "@kbn/managed-vscode-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-vscode-config plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_management_cards_navigation.mdx b/api_docs/kbn_management_cards_navigation.mdx index f495b862a1605..2820f77567156 100644 --- a/api_docs/kbn_management_cards_navigation.mdx +++ b/api_docs/kbn_management_cards_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-cards-navigation title: "@kbn/management-cards-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-cards-navigation plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-cards-navigation'] --- import kbnManagementCardsNavigationObj from './kbn_management_cards_navigation.devdocs.json'; diff --git a/api_docs/kbn_management_settings_application.mdx b/api_docs/kbn_management_settings_application.mdx index 1309f737b51e6..5c81e069f793f 100644 --- a/api_docs/kbn_management_settings_application.mdx +++ b/api_docs/kbn_management_settings_application.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-application title: "@kbn/management-settings-application" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-application plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-application'] --- import kbnManagementSettingsApplicationObj from './kbn_management_settings_application.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_category.mdx b/api_docs/kbn_management_settings_components_field_category.mdx index de2bfbafedce4..18ff351bd0894 100644 --- a/api_docs/kbn_management_settings_components_field_category.mdx +++ b/api_docs/kbn_management_settings_components_field_category.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-category title: "@kbn/management-settings-components-field-category" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-category plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-category'] --- import kbnManagementSettingsComponentsFieldCategoryObj from './kbn_management_settings_components_field_category.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_input.mdx b/api_docs/kbn_management_settings_components_field_input.mdx index 5bade5e28f2c5..db24e5df33bbd 100644 --- a/api_docs/kbn_management_settings_components_field_input.mdx +++ b/api_docs/kbn_management_settings_components_field_input.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-input title: "@kbn/management-settings-components-field-input" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-input plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-input'] --- import kbnManagementSettingsComponentsFieldInputObj from './kbn_management_settings_components_field_input.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_row.mdx b/api_docs/kbn_management_settings_components_field_row.mdx index 86ca4c62dca6b..265e6b05579ce 100644 --- a/api_docs/kbn_management_settings_components_field_row.mdx +++ b/api_docs/kbn_management_settings_components_field_row.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-row title: "@kbn/management-settings-components-field-row" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-row plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-row'] --- import kbnManagementSettingsComponentsFieldRowObj from './kbn_management_settings_components_field_row.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_form.mdx b/api_docs/kbn_management_settings_components_form.mdx index c72d6b06fc82f..1eef2b307fff2 100644 --- a/api_docs/kbn_management_settings_components_form.mdx +++ b/api_docs/kbn_management_settings_components_form.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-form title: "@kbn/management-settings-components-form" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-form plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-form'] --- import kbnManagementSettingsComponentsFormObj from './kbn_management_settings_components_form.devdocs.json'; diff --git a/api_docs/kbn_management_settings_field_definition.mdx b/api_docs/kbn_management_settings_field_definition.mdx index badf8774e0b76..43b61728fec52 100644 --- a/api_docs/kbn_management_settings_field_definition.mdx +++ b/api_docs/kbn_management_settings_field_definition.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-field-definition title: "@kbn/management-settings-field-definition" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-field-definition plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-field-definition'] --- import kbnManagementSettingsFieldDefinitionObj from './kbn_management_settings_field_definition.devdocs.json'; diff --git a/api_docs/kbn_management_settings_ids.mdx b/api_docs/kbn_management_settings_ids.mdx index 7dca968c44ec4..d10c6275c7361 100644 --- a/api_docs/kbn_management_settings_ids.mdx +++ b/api_docs/kbn_management_settings_ids.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-ids title: "@kbn/management-settings-ids" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-ids plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-ids'] --- import kbnManagementSettingsIdsObj from './kbn_management_settings_ids.devdocs.json'; diff --git a/api_docs/kbn_management_settings_section_registry.mdx b/api_docs/kbn_management_settings_section_registry.mdx index 6e646088d9b0e..1197351be5a93 100644 --- a/api_docs/kbn_management_settings_section_registry.mdx +++ b/api_docs/kbn_management_settings_section_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-section-registry title: "@kbn/management-settings-section-registry" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-section-registry plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-section-registry'] --- import kbnManagementSettingsSectionRegistryObj from './kbn_management_settings_section_registry.devdocs.json'; diff --git a/api_docs/kbn_management_settings_types.mdx b/api_docs/kbn_management_settings_types.mdx index 48227d1938f97..9bb669a65a9a9 100644 --- a/api_docs/kbn_management_settings_types.mdx +++ b/api_docs/kbn_management_settings_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-types title: "@kbn/management-settings-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-types plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-types'] --- import kbnManagementSettingsTypesObj from './kbn_management_settings_types.devdocs.json'; diff --git a/api_docs/kbn_management_settings_utilities.mdx b/api_docs/kbn_management_settings_utilities.mdx index 7cd7fd7cb6478..c3adef2b30de1 100644 --- a/api_docs/kbn_management_settings_utilities.mdx +++ b/api_docs/kbn_management_settings_utilities.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-utilities title: "@kbn/management-settings-utilities" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-utilities plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-utilities'] --- import kbnManagementSettingsUtilitiesObj from './kbn_management_settings_utilities.devdocs.json'; diff --git a/api_docs/kbn_management_storybook_config.mdx b/api_docs/kbn_management_storybook_config.mdx index 2ad8f016ee1c5..2720a7b68cbfe 100644 --- a/api_docs/kbn_management_storybook_config.mdx +++ b/api_docs/kbn_management_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-storybook-config title: "@kbn/management-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-storybook-config plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-storybook-config'] --- import kbnManagementStorybookConfigObj from './kbn_management_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_manifest.devdocs.json b/api_docs/kbn_manifest.devdocs.json new file mode 100644 index 0000000000000..d5098e14db2bf --- /dev/null +++ b/api_docs/kbn_manifest.devdocs.json @@ -0,0 +1,47 @@ +{ + "id": "@kbn/manifest", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/manifest", + "id": "def-server.runKbnManifestCli", + "type": "Function", + "tags": [], + "label": "runKbnManifestCli", + "description": [ + "\nA CLI to manipulate Kibana package manifest files" + ], + "signature": [ + "() => void" + ], + "path": "packages/kbn-manifest/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_manifest.mdx b/api_docs/kbn_manifest.mdx new file mode 100644 index 0000000000000..701c94824c8c6 --- /dev/null +++ b/api_docs/kbn_manifest.mdx @@ -0,0 +1,30 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnManifestPluginApi +slug: /kibana-dev-docs/api/kbn-manifest +title: "@kbn/manifest" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/manifest plugin +date: 2024-10-23 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/manifest'] +--- +import kbnManifestObj from './kbn_manifest.devdocs.json'; + + + +Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 1 | 0 | 0 | 0 | + +## Server + +### Functions + + diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index 749fc3680dc3f..98de155206042 100644 --- a/api_docs/kbn_mapbox_gl.mdx +++ b/api_docs/kbn_mapbox_gl.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mapbox-gl title: "@kbn/mapbox-gl" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mapbox-gl plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] --- import kbnMapboxGlObj from './kbn_mapbox_gl.devdocs.json'; diff --git a/api_docs/kbn_maps_vector_tile_utils.mdx b/api_docs/kbn_maps_vector_tile_utils.mdx index e3c002ea157ab..2aeb8f6080cad 100644 --- a/api_docs/kbn_maps_vector_tile_utils.mdx +++ b/api_docs/kbn_maps_vector_tile_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-maps-vector-tile-utils title: "@kbn/maps-vector-tile-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/maps-vector-tile-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/maps-vector-tile-utils'] --- import kbnMapsVectorTileUtilsObj from './kbn_maps_vector_tile_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index 3cf0ca7cd3b38..d04107c481375 100644 --- a/api_docs/kbn_ml_agg_utils.mdx +++ b/api_docs/kbn_ml_agg_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-agg-utils title: "@kbn/ml-agg-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-agg-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-agg-utils'] --- import kbnMlAggUtilsObj from './kbn_ml_agg_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_anomaly_utils.mdx b/api_docs/kbn_ml_anomaly_utils.mdx index c02b8555ec6a7..fcb0e68bf6f36 100644 --- a/api_docs/kbn_ml_anomaly_utils.mdx +++ b/api_docs/kbn_ml_anomaly_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-anomaly-utils title: "@kbn/ml-anomaly-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-anomaly-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-anomaly-utils'] --- import kbnMlAnomalyUtilsObj from './kbn_ml_anomaly_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_cancellable_search.mdx b/api_docs/kbn_ml_cancellable_search.mdx index 2eef181870bec..2ca1a387547c1 100644 --- a/api_docs/kbn_ml_cancellable_search.mdx +++ b/api_docs/kbn_ml_cancellable_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-cancellable-search title: "@kbn/ml-cancellable-search" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-cancellable-search plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-cancellable-search'] --- import kbnMlCancellableSearchObj from './kbn_ml_cancellable_search.devdocs.json'; diff --git a/api_docs/kbn_ml_category_validator.mdx b/api_docs/kbn_ml_category_validator.mdx index 747bb233d4641..795ec79c3e1c8 100644 --- a/api_docs/kbn_ml_category_validator.mdx +++ b/api_docs/kbn_ml_category_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-category-validator title: "@kbn/ml-category-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-category-validator plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-category-validator'] --- import kbnMlCategoryValidatorObj from './kbn_ml_category_validator.devdocs.json'; diff --git a/api_docs/kbn_ml_chi2test.mdx b/api_docs/kbn_ml_chi2test.mdx index 618ef5468ce4b..40985831652d3 100644 --- a/api_docs/kbn_ml_chi2test.mdx +++ b/api_docs/kbn_ml_chi2test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-chi2test title: "@kbn/ml-chi2test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-chi2test plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-chi2test'] --- import kbnMlChi2testObj from './kbn_ml_chi2test.devdocs.json'; diff --git a/api_docs/kbn_ml_data_frame_analytics_utils.mdx b/api_docs/kbn_ml_data_frame_analytics_utils.mdx index 5a49f1bf076a2..fb7c66e45880f 100644 --- a/api_docs/kbn_ml_data_frame_analytics_utils.mdx +++ b/api_docs/kbn_ml_data_frame_analytics_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-frame-analytics-utils title: "@kbn/ml-data-frame-analytics-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-frame-analytics-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-data-frame-analytics-utils'] --- import kbnMlDataFrameAnalyticsUtilsObj from './kbn_ml_data_frame_analytics_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_data_grid.mdx b/api_docs/kbn_ml_data_grid.mdx index ec5ad5f255e04..fa008ba8445c1 100644 --- a/api_docs/kbn_ml_data_grid.mdx +++ b/api_docs/kbn_ml_data_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-grid title: "@kbn/ml-data-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-grid plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-data-grid'] --- import kbnMlDataGridObj from './kbn_ml_data_grid.devdocs.json'; diff --git a/api_docs/kbn_ml_date_picker.mdx b/api_docs/kbn_ml_date_picker.mdx index 6c14f40d237d2..f62be719afc5c 100644 --- a/api_docs/kbn_ml_date_picker.mdx +++ b/api_docs/kbn_ml_date_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-picker title: "@kbn/ml-date-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-picker plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-picker'] --- import kbnMlDatePickerObj from './kbn_ml_date_picker.devdocs.json'; diff --git a/api_docs/kbn_ml_date_utils.mdx b/api_docs/kbn_ml_date_utils.mdx index 36c164d8de13a..50486b789de61 100644 --- a/api_docs/kbn_ml_date_utils.mdx +++ b/api_docs/kbn_ml_date_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-utils title: "@kbn/ml-date-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-utils'] --- import kbnMlDateUtilsObj from './kbn_ml_date_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_error_utils.mdx b/api_docs/kbn_ml_error_utils.mdx index aef737dfd581e..7d9f10b5ba477 100644 --- a/api_docs/kbn_ml_error_utils.mdx +++ b/api_docs/kbn_ml_error_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-error-utils title: "@kbn/ml-error-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-error-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-error-utils'] --- import kbnMlErrorUtilsObj from './kbn_ml_error_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_field_stats_flyout.mdx b/api_docs/kbn_ml_field_stats_flyout.mdx index 6635f38a93009..d6470180e010a 100644 --- a/api_docs/kbn_ml_field_stats_flyout.mdx +++ b/api_docs/kbn_ml_field_stats_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-field-stats-flyout title: "@kbn/ml-field-stats-flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-field-stats-flyout plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-field-stats-flyout'] --- import kbnMlFieldStatsFlyoutObj from './kbn_ml_field_stats_flyout.devdocs.json'; diff --git a/api_docs/kbn_ml_in_memory_table.mdx b/api_docs/kbn_ml_in_memory_table.mdx index b3501746b635f..9ee8c41f82b23 100644 --- a/api_docs/kbn_ml_in_memory_table.mdx +++ b/api_docs/kbn_ml_in_memory_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-in-memory-table title: "@kbn/ml-in-memory-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-in-memory-table plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-in-memory-table'] --- import kbnMlInMemoryTableObj from './kbn_ml_in_memory_table.devdocs.json'; diff --git a/api_docs/kbn_ml_is_defined.mdx b/api_docs/kbn_ml_is_defined.mdx index 8c4e275083e52..8386ff75586e0 100644 --- a/api_docs/kbn_ml_is_defined.mdx +++ b/api_docs/kbn_ml_is_defined.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-defined title: "@kbn/ml-is-defined" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-defined plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-defined'] --- import kbnMlIsDefinedObj from './kbn_ml_is_defined.devdocs.json'; diff --git a/api_docs/kbn_ml_is_populated_object.mdx b/api_docs/kbn_ml_is_populated_object.mdx index 571bae27b1c7b..26da72a924e9f 100644 --- a/api_docs/kbn_ml_is_populated_object.mdx +++ b/api_docs/kbn_ml_is_populated_object.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-populated-object title: "@kbn/ml-is-populated-object" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-populated-object plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-populated-object'] --- import kbnMlIsPopulatedObjectObj from './kbn_ml_is_populated_object.devdocs.json'; diff --git a/api_docs/kbn_ml_kibana_theme.mdx b/api_docs/kbn_ml_kibana_theme.mdx index 39057e72da0f2..f467b44a4f08a 100644 --- a/api_docs/kbn_ml_kibana_theme.mdx +++ b/api_docs/kbn_ml_kibana_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-kibana-theme title: "@kbn/ml-kibana-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-kibana-theme plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-kibana-theme'] --- import kbnMlKibanaThemeObj from './kbn_ml_kibana_theme.devdocs.json'; diff --git a/api_docs/kbn_ml_local_storage.mdx b/api_docs/kbn_ml_local_storage.mdx index 454a58a740a82..4d844697c3344 100644 --- a/api_docs/kbn_ml_local_storage.mdx +++ b/api_docs/kbn_ml_local_storage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-local-storage title: "@kbn/ml-local-storage" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-local-storage plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-local-storage'] --- import kbnMlLocalStorageObj from './kbn_ml_local_storage.devdocs.json'; diff --git a/api_docs/kbn_ml_nested_property.mdx b/api_docs/kbn_ml_nested_property.mdx index eb7dcfa9a5408..635ce00feae8a 100644 --- a/api_docs/kbn_ml_nested_property.mdx +++ b/api_docs/kbn_ml_nested_property.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-nested-property title: "@kbn/ml-nested-property" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-nested-property plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-nested-property'] --- import kbnMlNestedPropertyObj from './kbn_ml_nested_property.devdocs.json'; diff --git a/api_docs/kbn_ml_number_utils.mdx b/api_docs/kbn_ml_number_utils.mdx index c2aee5e73fadc..ecffdbf4fba30 100644 --- a/api_docs/kbn_ml_number_utils.mdx +++ b/api_docs/kbn_ml_number_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-number-utils title: "@kbn/ml-number-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-number-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-number-utils'] --- import kbnMlNumberUtilsObj from './kbn_ml_number_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_parse_interval.mdx b/api_docs/kbn_ml_parse_interval.mdx index 3e5bc877d51f6..c28f00d61ad78 100644 --- a/api_docs/kbn_ml_parse_interval.mdx +++ b/api_docs/kbn_ml_parse_interval.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-parse-interval title: "@kbn/ml-parse-interval" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-parse-interval plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-parse-interval'] --- import kbnMlParseIntervalObj from './kbn_ml_parse_interval.devdocs.json'; diff --git a/api_docs/kbn_ml_query_utils.mdx b/api_docs/kbn_ml_query_utils.mdx index 47cc714180d62..2b87406107560 100644 --- a/api_docs/kbn_ml_query_utils.mdx +++ b/api_docs/kbn_ml_query_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-query-utils title: "@kbn/ml-query-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-query-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-query-utils'] --- import kbnMlQueryUtilsObj from './kbn_ml_query_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_random_sampler_utils.mdx b/api_docs/kbn_ml_random_sampler_utils.mdx index cc18f219cddf5..1ca830e0e04df 100644 --- a/api_docs/kbn_ml_random_sampler_utils.mdx +++ b/api_docs/kbn_ml_random_sampler_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-random-sampler-utils title: "@kbn/ml-random-sampler-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-random-sampler-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-random-sampler-utils'] --- import kbnMlRandomSamplerUtilsObj from './kbn_ml_random_sampler_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_route_utils.mdx b/api_docs/kbn_ml_route_utils.mdx index 826541a7a5448..bca7d10654381 100644 --- a/api_docs/kbn_ml_route_utils.mdx +++ b/api_docs/kbn_ml_route_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-route-utils title: "@kbn/ml-route-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-route-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-route-utils'] --- import kbnMlRouteUtilsObj from './kbn_ml_route_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_runtime_field_utils.mdx b/api_docs/kbn_ml_runtime_field_utils.mdx index 5c819ec9fdbbc..e5b8f230d38f2 100644 --- a/api_docs/kbn_ml_runtime_field_utils.mdx +++ b/api_docs/kbn_ml_runtime_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-runtime-field-utils title: "@kbn/ml-runtime-field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-runtime-field-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-runtime-field-utils'] --- import kbnMlRuntimeFieldUtilsObj from './kbn_ml_runtime_field_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index c12b41099c7ed..9865b072fdf14 100644 --- a/api_docs/kbn_ml_string_hash.mdx +++ b/api_docs/kbn_ml_string_hash.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-string-hash title: "@kbn/ml-string-hash" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-string-hash plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_ml_time_buckets.mdx b/api_docs/kbn_ml_time_buckets.mdx index e26ef89589986..f3c73e365f533 100644 --- a/api_docs/kbn_ml_time_buckets.mdx +++ b/api_docs/kbn_ml_time_buckets.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-time-buckets title: "@kbn/ml-time-buckets" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-time-buckets plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-time-buckets'] --- import kbnMlTimeBucketsObj from './kbn_ml_time_buckets.devdocs.json'; diff --git a/api_docs/kbn_ml_trained_models_utils.mdx b/api_docs/kbn_ml_trained_models_utils.mdx index 1379a87a63258..65d9f74292b34 100644 --- a/api_docs/kbn_ml_trained_models_utils.mdx +++ b/api_docs/kbn_ml_trained_models_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-trained-models-utils title: "@kbn/ml-trained-models-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-trained-models-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-trained-models-utils'] --- import kbnMlTrainedModelsUtilsObj from './kbn_ml_trained_models_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_ui_actions.mdx b/api_docs/kbn_ml_ui_actions.mdx index 7749d2f9da996..9e22cee8e6bdd 100644 --- a/api_docs/kbn_ml_ui_actions.mdx +++ b/api_docs/kbn_ml_ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-ui-actions title: "@kbn/ml-ui-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-ui-actions plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-ui-actions'] --- import kbnMlUiActionsObj from './kbn_ml_ui_actions.devdocs.json'; diff --git a/api_docs/kbn_ml_url_state.mdx b/api_docs/kbn_ml_url_state.mdx index 8bad2f16c220c..bfb7db77ec93e 100644 --- a/api_docs/kbn_ml_url_state.mdx +++ b/api_docs/kbn_ml_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-url-state title: "@kbn/ml-url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-url-state plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-url-state'] --- import kbnMlUrlStateObj from './kbn_ml_url_state.devdocs.json'; diff --git a/api_docs/kbn_ml_validators.mdx b/api_docs/kbn_ml_validators.mdx index bdd3f7b3f44ed..089fd47272cc5 100644 --- a/api_docs/kbn_ml_validators.mdx +++ b/api_docs/kbn_ml_validators.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-validators title: "@kbn/ml-validators" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-validators plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-validators'] --- import kbnMlValidatorsObj from './kbn_ml_validators.devdocs.json'; diff --git a/api_docs/kbn_mock_idp_utils.mdx b/api_docs/kbn_mock_idp_utils.mdx index 88974c086ef41..d3cd4e13073ce 100644 --- a/api_docs/kbn_mock_idp_utils.mdx +++ b/api_docs/kbn_mock_idp_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mock-idp-utils title: "@kbn/mock-idp-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mock-idp-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mock-idp-utils'] --- import kbnMockIdpUtilsObj from './kbn_mock_idp_utils.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index c5ca0d961296c..dc626ad8492b5 100644 --- a/api_docs/kbn_monaco.mdx +++ b/api_docs/kbn_monaco.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-monaco title: "@kbn/monaco" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/monaco plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_object_versioning.mdx b/api_docs/kbn_object_versioning.mdx index c6898a26d7f28..4d15309731dd0 100644 --- a/api_docs/kbn_object_versioning.mdx +++ b/api_docs/kbn_object_versioning.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-object-versioning title: "@kbn/object-versioning" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/object-versioning plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/object-versioning'] --- import kbnObjectVersioningObj from './kbn_object_versioning.devdocs.json'; diff --git a/api_docs/kbn_object_versioning_utils.mdx b/api_docs/kbn_object_versioning_utils.mdx index 1b6efb1fcdee3..6b4731d6b77be 100644 --- a/api_docs/kbn_object_versioning_utils.mdx +++ b/api_docs/kbn_object_versioning_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-object-versioning-utils title: "@kbn/object-versioning-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/object-versioning-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/object-versioning-utils'] --- import kbnObjectVersioningUtilsObj from './kbn_object_versioning_utils.devdocs.json'; diff --git a/api_docs/kbn_observability_alert_details.mdx b/api_docs/kbn_observability_alert_details.mdx index e49295e144724..6f7fd9448b9a3 100644 --- a/api_docs/kbn_observability_alert_details.mdx +++ b/api_docs/kbn_observability_alert_details.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alert-details title: "@kbn/observability-alert-details" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alert-details plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alert-details'] --- import kbnObservabilityAlertDetailsObj from './kbn_observability_alert_details.devdocs.json'; diff --git a/api_docs/kbn_observability_alerting_rule_utils.mdx b/api_docs/kbn_observability_alerting_rule_utils.mdx index a7d04ffe65fd2..a4909cef8ac5a 100644 --- a/api_docs/kbn_observability_alerting_rule_utils.mdx +++ b/api_docs/kbn_observability_alerting_rule_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alerting-rule-utils title: "@kbn/observability-alerting-rule-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alerting-rule-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alerting-rule-utils'] --- import kbnObservabilityAlertingRuleUtilsObj from './kbn_observability_alerting_rule_utils.devdocs.json'; diff --git a/api_docs/kbn_observability_alerting_test_data.mdx b/api_docs/kbn_observability_alerting_test_data.mdx index 38903748acb60..60227e84201e5 100644 --- a/api_docs/kbn_observability_alerting_test_data.mdx +++ b/api_docs/kbn_observability_alerting_test_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alerting-test-data title: "@kbn/observability-alerting-test-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alerting-test-data plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alerting-test-data'] --- import kbnObservabilityAlertingTestDataObj from './kbn_observability_alerting_test_data.devdocs.json'; diff --git a/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx b/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx index cc40f03ccdc45..cd099c16473db 100644 --- a/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx +++ b/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-get-padded-alert-time-range-util title: "@kbn/observability-get-padded-alert-time-range-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-get-padded-alert-time-range-util plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-get-padded-alert-time-range-util'] --- import kbnObservabilityGetPaddedAlertTimeRangeUtilObj from './kbn_observability_get_padded_alert_time_range_util.devdocs.json'; diff --git a/api_docs/kbn_observability_logs_overview.mdx b/api_docs/kbn_observability_logs_overview.mdx index 85ccc1f667152..1d288f077cd1e 100644 --- a/api_docs/kbn_observability_logs_overview.mdx +++ b/api_docs/kbn_observability_logs_overview.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-logs-overview title: "@kbn/observability-logs-overview" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-logs-overview plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-logs-overview'] --- import kbnObservabilityLogsOverviewObj from './kbn_observability_logs_overview.devdocs.json'; diff --git a/api_docs/kbn_observability_synthetics_test_data.mdx b/api_docs/kbn_observability_synthetics_test_data.mdx index 8b4c059e9e6b3..018ca688ec568 100644 --- a/api_docs/kbn_observability_synthetics_test_data.mdx +++ b/api_docs/kbn_observability_synthetics_test_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-synthetics-test-data title: "@kbn/observability-synthetics-test-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-synthetics-test-data plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-synthetics-test-data'] --- import kbnObservabilitySyntheticsTestDataObj from './kbn_observability_synthetics_test_data.devdocs.json'; diff --git a/api_docs/kbn_openapi_bundler.mdx b/api_docs/kbn_openapi_bundler.mdx index c6f4a0127f763..7b87d48fe3a25 100644 --- a/api_docs/kbn_openapi_bundler.mdx +++ b/api_docs/kbn_openapi_bundler.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-openapi-bundler title: "@kbn/openapi-bundler" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/openapi-bundler plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/openapi-bundler'] --- import kbnOpenapiBundlerObj from './kbn_openapi_bundler.devdocs.json'; diff --git a/api_docs/kbn_openapi_generator.mdx b/api_docs/kbn_openapi_generator.mdx index 79274873f8d5b..8d1dad787fa9d 100644 --- a/api_docs/kbn_openapi_generator.mdx +++ b/api_docs/kbn_openapi_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-openapi-generator title: "@kbn/openapi-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/openapi-generator plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/openapi-generator'] --- import kbnOpenapiGeneratorObj from './kbn_openapi_generator.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index 670193059d090..53d44e62876fc 100644 --- a/api_docs/kbn_optimizer.mdx +++ b/api_docs/kbn_optimizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer title: "@kbn/optimizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer'] --- import kbnOptimizerObj from './kbn_optimizer.devdocs.json'; diff --git a/api_docs/kbn_optimizer_webpack_helpers.mdx b/api_docs/kbn_optimizer_webpack_helpers.mdx index ff840eb02e8bc..9a70bab522f7a 100644 --- a/api_docs/kbn_optimizer_webpack_helpers.mdx +++ b/api_docs/kbn_optimizer_webpack_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer-webpack-helpers title: "@kbn/optimizer-webpack-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer-webpack-helpers plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer-webpack-helpers'] --- import kbnOptimizerWebpackHelpersObj from './kbn_optimizer_webpack_helpers.devdocs.json'; diff --git a/api_docs/kbn_osquery_io_ts_types.mdx b/api_docs/kbn_osquery_io_ts_types.mdx index e26e8a177c2ca..c25daa030b7c9 100644 --- a/api_docs/kbn_osquery_io_ts_types.mdx +++ b/api_docs/kbn_osquery_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-osquery-io-ts-types title: "@kbn/osquery-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/osquery-io-ts-types plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/osquery-io-ts-types'] --- import kbnOsqueryIoTsTypesObj from './kbn_osquery_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_panel_loader.mdx b/api_docs/kbn_panel_loader.mdx index 0530bdf3f6c73..0faedb06d0be6 100644 --- a/api_docs/kbn_panel_loader.mdx +++ b/api_docs/kbn_panel_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-panel-loader title: "@kbn/panel-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/panel-loader plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/panel-loader'] --- import kbnPanelLoaderObj from './kbn_panel_loader.devdocs.json'; diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index d53088aa749a9..ef190c222fffe 100644 --- a/api_docs/kbn_performance_testing_dataset_extractor.mdx +++ b/api_docs/kbn_performance_testing_dataset_extractor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-performance-testing-dataset-extractor title: "@kbn/performance-testing-dataset-extractor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/performance-testing-dataset-extractor plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/performance-testing-dataset-extractor'] --- import kbnPerformanceTestingDatasetExtractorObj from './kbn_performance_testing_dataset_extractor.devdocs.json'; diff --git a/api_docs/kbn_plugin_check.mdx b/api_docs/kbn_plugin_check.mdx index 7d7d0f76e892e..fc5c65896dffb 100644 --- a/api_docs/kbn_plugin_check.mdx +++ b/api_docs/kbn_plugin_check.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-check title: "@kbn/plugin-check" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-check plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-check'] --- import kbnPluginCheckObj from './kbn_plugin_check.devdocs.json'; diff --git a/api_docs/kbn_plugin_generator.mdx b/api_docs/kbn_plugin_generator.mdx index 8bb78397b100d..e085fb1556604 100644 --- a/api_docs/kbn_plugin_generator.mdx +++ b/api_docs/kbn_plugin_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-generator title: "@kbn/plugin-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-generator plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-generator'] --- import kbnPluginGeneratorObj from './kbn_plugin_generator.devdocs.json'; diff --git a/api_docs/kbn_plugin_helpers.mdx b/api_docs/kbn_plugin_helpers.mdx index 7123e4649af9a..8a9c9b8af87ba 100644 --- a/api_docs/kbn_plugin_helpers.mdx +++ b/api_docs/kbn_plugin_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-helpers title: "@kbn/plugin-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-helpers plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_presentation_containers.mdx b/api_docs/kbn_presentation_containers.mdx index f68478f3b52e0..544ebcc88f5b6 100644 --- a/api_docs/kbn_presentation_containers.mdx +++ b/api_docs/kbn_presentation_containers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-presentation-containers title: "@kbn/presentation-containers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/presentation-containers plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/presentation-containers'] --- import kbnPresentationContainersObj from './kbn_presentation_containers.devdocs.json'; diff --git a/api_docs/kbn_presentation_publishing.mdx b/api_docs/kbn_presentation_publishing.mdx index 694fc752c228d..0be85e7d48eb8 100644 --- a/api_docs/kbn_presentation_publishing.mdx +++ b/api_docs/kbn_presentation_publishing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-presentation-publishing title: "@kbn/presentation-publishing" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/presentation-publishing plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/presentation-publishing'] --- import kbnPresentationPublishingObj from './kbn_presentation_publishing.devdocs.json'; diff --git a/api_docs/kbn_product_doc_artifact_builder.mdx b/api_docs/kbn_product_doc_artifact_builder.mdx index b5dee21d5828e..e3ae09c7a122d 100644 --- a/api_docs/kbn_product_doc_artifact_builder.mdx +++ b/api_docs/kbn_product_doc_artifact_builder.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-product-doc-artifact-builder title: "@kbn/product-doc-artifact-builder" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/product-doc-artifact-builder plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/product-doc-artifact-builder'] --- import kbnProductDocArtifactBuilderObj from './kbn_product_doc_artifact_builder.devdocs.json'; diff --git a/api_docs/kbn_profiling_utils.mdx b/api_docs/kbn_profiling_utils.mdx index 1bea9be5f27c7..1c38918dae4f4 100644 --- a/api_docs/kbn_profiling_utils.mdx +++ b/api_docs/kbn_profiling_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-profiling-utils title: "@kbn/profiling-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/profiling-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/profiling-utils'] --- import kbnProfilingUtilsObj from './kbn_profiling_utils.devdocs.json'; diff --git a/api_docs/kbn_random_sampling.mdx b/api_docs/kbn_random_sampling.mdx index db8b3678d75b0..e845fba51eba6 100644 --- a/api_docs/kbn_random_sampling.mdx +++ b/api_docs/kbn_random_sampling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-random-sampling title: "@kbn/random-sampling" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/random-sampling plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/random-sampling'] --- import kbnRandomSamplingObj from './kbn_random_sampling.devdocs.json'; diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index c9b603726cff3..e8ae5484895b7 100644 --- a/api_docs/kbn_react_field.mdx +++ b/api_docs/kbn_react_field.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-field title: "@kbn/react-field" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-field plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_react_hooks.mdx b/api_docs/kbn_react_hooks.mdx index 2d604e72fb47d..103b80256875b 100644 --- a/api_docs/kbn_react_hooks.mdx +++ b/api_docs/kbn_react_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-hooks title: "@kbn/react-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-hooks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-hooks'] --- import kbnReactHooksObj from './kbn_react_hooks.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_common.mdx b/api_docs/kbn_react_kibana_context_common.mdx index 6d8993bbb06d8..b5f72707daf28 100644 --- a/api_docs/kbn_react_kibana_context_common.mdx +++ b/api_docs/kbn_react_kibana_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-common title: "@kbn/react-kibana-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-common plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-common'] --- import kbnReactKibanaContextCommonObj from './kbn_react_kibana_context_common.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_render.mdx b/api_docs/kbn_react_kibana_context_render.mdx index 8fcaca1af0867..8e3be04d309fa 100644 --- a/api_docs/kbn_react_kibana_context_render.mdx +++ b/api_docs/kbn_react_kibana_context_render.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-render title: "@kbn/react-kibana-context-render" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-render plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-render'] --- import kbnReactKibanaContextRenderObj from './kbn_react_kibana_context_render.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_root.mdx b/api_docs/kbn_react_kibana_context_root.mdx index 440a67765fe50..c0c26b5f8adf5 100644 --- a/api_docs/kbn_react_kibana_context_root.mdx +++ b/api_docs/kbn_react_kibana_context_root.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-root title: "@kbn/react-kibana-context-root" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-root plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-root'] --- import kbnReactKibanaContextRootObj from './kbn_react_kibana_context_root.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_styled.mdx b/api_docs/kbn_react_kibana_context_styled.mdx index 90aa3f13f803e..6b1974a6dd74b 100644 --- a/api_docs/kbn_react_kibana_context_styled.mdx +++ b/api_docs/kbn_react_kibana_context_styled.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-styled title: "@kbn/react-kibana-context-styled" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-styled plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-styled'] --- import kbnReactKibanaContextStyledObj from './kbn_react_kibana_context_styled.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_theme.mdx b/api_docs/kbn_react_kibana_context_theme.mdx index 31ba1f6c218e8..53d592bbb7535 100644 --- a/api_docs/kbn_react_kibana_context_theme.mdx +++ b/api_docs/kbn_react_kibana_context_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-theme title: "@kbn/react-kibana-context-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-theme plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-theme'] --- import kbnReactKibanaContextThemeObj from './kbn_react_kibana_context_theme.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_mount.mdx b/api_docs/kbn_react_kibana_mount.mdx index 1ef171aa3cdae..c401af9213205 100644 --- a/api_docs/kbn_react_kibana_mount.mdx +++ b/api_docs/kbn_react_kibana_mount.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-mount title: "@kbn/react-kibana-mount" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-mount plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-mount'] --- import kbnReactKibanaMountObj from './kbn_react_kibana_mount.devdocs.json'; diff --git a/api_docs/kbn_recently_accessed.mdx b/api_docs/kbn_recently_accessed.mdx index da2197581e2ae..7a833ea6508ad 100644 --- a/api_docs/kbn_recently_accessed.mdx +++ b/api_docs/kbn_recently_accessed.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-recently-accessed title: "@kbn/recently-accessed" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/recently-accessed plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/recently-accessed'] --- import kbnRecentlyAccessedObj from './kbn_recently_accessed.devdocs.json'; diff --git a/api_docs/kbn_repo_file_maps.mdx b/api_docs/kbn_repo_file_maps.mdx index 7c561b381a1a4..f8c7cbd52bc65 100644 --- a/api_docs/kbn_repo_file_maps.mdx +++ b/api_docs/kbn_repo_file_maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-file-maps title: "@kbn/repo-file-maps" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-file-maps plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-file-maps'] --- import kbnRepoFileMapsObj from './kbn_repo_file_maps.devdocs.json'; diff --git a/api_docs/kbn_repo_linter.mdx b/api_docs/kbn_repo_linter.mdx index 195c48c38d95e..3f18186f7d896 100644 --- a/api_docs/kbn_repo_linter.mdx +++ b/api_docs/kbn_repo_linter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-linter title: "@kbn/repo-linter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-linter plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-linter'] --- import kbnRepoLinterObj from './kbn_repo_linter.devdocs.json'; diff --git a/api_docs/kbn_repo_path.mdx b/api_docs/kbn_repo_path.mdx index b59eaa3a38a43..dc69fdff1248e 100644 --- a/api_docs/kbn_repo_path.mdx +++ b/api_docs/kbn_repo_path.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-path title: "@kbn/repo-path" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-path plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-path'] --- import kbnRepoPathObj from './kbn_repo_path.devdocs.json'; diff --git a/api_docs/kbn_repo_source_classifier.mdx b/api_docs/kbn_repo_source_classifier.mdx index e57ef3a4aa995..e431fa380894e 100644 --- a/api_docs/kbn_repo_source_classifier.mdx +++ b/api_docs/kbn_repo_source_classifier.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-source-classifier title: "@kbn/repo-source-classifier" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-source-classifier plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] --- import kbnRepoSourceClassifierObj from './kbn_repo_source_classifier.devdocs.json'; diff --git a/api_docs/kbn_reporting_common.devdocs.json b/api_docs/kbn_reporting_common.devdocs.json index 7139afb78c24e..b7b20c613c82a 100644 --- a/api_docs/kbn_reporting_common.devdocs.json +++ b/api_docs/kbn_reporting_common.devdocs.json @@ -758,6 +758,62 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.ReportingSavedObjectNotFoundError", + "type": "Class", + "tags": [], + "label": "ReportingSavedObjectNotFoundError", + "description": [], + "signature": [ + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.ReportingSavedObjectNotFoundError", + "text": "ReportingSavedObjectNotFoundError" + }, + " extends ", + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.ReportingError", + "text": "ReportingError" + } + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.ReportingSavedObjectNotFoundError.code", + "type": "string", + "tags": [], + "label": "code", + "description": [], + "signature": [ + "\"reporting_saved_object_not_found_error\"" + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.ReportingSavedObjectNotFoundError.code", + "type": "string", + "tags": [], + "label": "code", + "description": [], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/reporting-common", "id": "def-common.UnknownError", diff --git a/api_docs/kbn_reporting_common.mdx b/api_docs/kbn_reporting_common.mdx index 5e8bb0474c3a3..bacbadd071a1a 100644 --- a/api_docs/kbn_reporting_common.mdx +++ b/api_docs/kbn_reporting_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-common title: "@kbn/reporting-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-common plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-common'] --- import kbnReportingCommonObj from './kbn_reporting_common.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sh | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 96 | 0 | 86 | 13 | +| 99 | 0 | 89 | 13 | ## Common diff --git a/api_docs/kbn_reporting_csv_share_panel.mdx b/api_docs/kbn_reporting_csv_share_panel.mdx index 388b0a94a71a3..0fee8b5f8f191 100644 --- a/api_docs/kbn_reporting_csv_share_panel.mdx +++ b/api_docs/kbn_reporting_csv_share_panel.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-csv-share-panel title: "@kbn/reporting-csv-share-panel" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-csv-share-panel plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-csv-share-panel'] --- import kbnReportingCsvSharePanelObj from './kbn_reporting_csv_share_panel.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_csv.mdx b/api_docs/kbn_reporting_export_types_csv.mdx index 567d5cb44af8c..8e01711eff9bd 100644 --- a/api_docs/kbn_reporting_export_types_csv.mdx +++ b/api_docs/kbn_reporting_export_types_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-csv title: "@kbn/reporting-export-types-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-csv plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-csv'] --- import kbnReportingExportTypesCsvObj from './kbn_reporting_export_types_csv.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_csv_common.mdx b/api_docs/kbn_reporting_export_types_csv_common.mdx index f375c16a9d66e..a19ca891c8559 100644 --- a/api_docs/kbn_reporting_export_types_csv_common.mdx +++ b/api_docs/kbn_reporting_export_types_csv_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-csv-common title: "@kbn/reporting-export-types-csv-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-csv-common plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-csv-common'] --- import kbnReportingExportTypesCsvCommonObj from './kbn_reporting_export_types_csv_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_pdf.mdx b/api_docs/kbn_reporting_export_types_pdf.mdx index c6e6be952f09a..cc6e879b08d22 100644 --- a/api_docs/kbn_reporting_export_types_pdf.mdx +++ b/api_docs/kbn_reporting_export_types_pdf.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-pdf title: "@kbn/reporting-export-types-pdf" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-pdf plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-pdf'] --- import kbnReportingExportTypesPdfObj from './kbn_reporting_export_types_pdf.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_pdf_common.mdx b/api_docs/kbn_reporting_export_types_pdf_common.mdx index 068cc43284918..fbd7a6eeec632 100644 --- a/api_docs/kbn_reporting_export_types_pdf_common.mdx +++ b/api_docs/kbn_reporting_export_types_pdf_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-pdf-common title: "@kbn/reporting-export-types-pdf-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-pdf-common plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-pdf-common'] --- import kbnReportingExportTypesPdfCommonObj from './kbn_reporting_export_types_pdf_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_png.mdx b/api_docs/kbn_reporting_export_types_png.mdx index 820acf06366e6..8740a98329d9e 100644 --- a/api_docs/kbn_reporting_export_types_png.mdx +++ b/api_docs/kbn_reporting_export_types_png.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-png title: "@kbn/reporting-export-types-png" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-png plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-png'] --- import kbnReportingExportTypesPngObj from './kbn_reporting_export_types_png.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_png_common.mdx b/api_docs/kbn_reporting_export_types_png_common.mdx index e2b9de7ccb749..251ee85580d57 100644 --- a/api_docs/kbn_reporting_export_types_png_common.mdx +++ b/api_docs/kbn_reporting_export_types_png_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-png-common title: "@kbn/reporting-export-types-png-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-png-common plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-png-common'] --- import kbnReportingExportTypesPngCommonObj from './kbn_reporting_export_types_png_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_mocks_server.mdx b/api_docs/kbn_reporting_mocks_server.mdx index 88125ec278bc0..cf00f6402142d 100644 --- a/api_docs/kbn_reporting_mocks_server.mdx +++ b/api_docs/kbn_reporting_mocks_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-mocks-server title: "@kbn/reporting-mocks-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-mocks-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-mocks-server'] --- import kbnReportingMocksServerObj from './kbn_reporting_mocks_server.devdocs.json'; diff --git a/api_docs/kbn_reporting_public.mdx b/api_docs/kbn_reporting_public.mdx index 5a84407c64322..a6b1728182ef7 100644 --- a/api_docs/kbn_reporting_public.mdx +++ b/api_docs/kbn_reporting_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-public title: "@kbn/reporting-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-public plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-public'] --- import kbnReportingPublicObj from './kbn_reporting_public.devdocs.json'; diff --git a/api_docs/kbn_reporting_server.mdx b/api_docs/kbn_reporting_server.mdx index dc9d597cba0a9..66eb40bdf13af 100644 --- a/api_docs/kbn_reporting_server.mdx +++ b/api_docs/kbn_reporting_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-server title: "@kbn/reporting-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-server'] --- import kbnReportingServerObj from './kbn_reporting_server.devdocs.json'; diff --git a/api_docs/kbn_resizable_layout.mdx b/api_docs/kbn_resizable_layout.mdx index f8522cb23f93b..01e739b2eea59 100644 --- a/api_docs/kbn_resizable_layout.mdx +++ b/api_docs/kbn_resizable_layout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-resizable-layout title: "@kbn/resizable-layout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/resizable-layout plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/resizable-layout'] --- import kbnResizableLayoutObj from './kbn_resizable_layout.devdocs.json'; diff --git a/api_docs/kbn_response_ops_feature_flag_service.mdx b/api_docs/kbn_response_ops_feature_flag_service.mdx index a6d3b0e18bfda..27107dd30cca2 100644 --- a/api_docs/kbn_response_ops_feature_flag_service.mdx +++ b/api_docs/kbn_response_ops_feature_flag_service.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-response-ops-feature-flag-service title: "@kbn/response-ops-feature-flag-service" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/response-ops-feature-flag-service plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/response-ops-feature-flag-service'] --- import kbnResponseOpsFeatureFlagServiceObj from './kbn_response_ops_feature_flag_service.devdocs.json'; diff --git a/api_docs/kbn_rison.mdx b/api_docs/kbn_rison.mdx index 466151c9b37af..7fa1a5feb45aa 100644 --- a/api_docs/kbn_rison.mdx +++ b/api_docs/kbn_rison.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rison title: "@kbn/rison" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rison plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rison'] --- import kbnRisonObj from './kbn_rison.devdocs.json'; diff --git a/api_docs/kbn_rollup.mdx b/api_docs/kbn_rollup.mdx index 43cb6d7864a4b..71c199c3e73f6 100644 --- a/api_docs/kbn_rollup.mdx +++ b/api_docs/kbn_rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rollup title: "@kbn/rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rollup plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rollup'] --- import kbnRollupObj from './kbn_rollup.devdocs.json'; diff --git a/api_docs/kbn_router_to_openapispec.mdx b/api_docs/kbn_router_to_openapispec.mdx index cadd87fdcaaf8..b9d2fdf7eadbf 100644 --- a/api_docs/kbn_router_to_openapispec.mdx +++ b/api_docs/kbn_router_to_openapispec.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-router-to-openapispec title: "@kbn/router-to-openapispec" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/router-to-openapispec plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/router-to-openapispec'] --- import kbnRouterToOpenapispecObj from './kbn_router_to_openapispec.devdocs.json'; diff --git a/api_docs/kbn_router_utils.mdx b/api_docs/kbn_router_utils.mdx index fd07e83107dec..6cde5255c91e9 100644 --- a/api_docs/kbn_router_utils.mdx +++ b/api_docs/kbn_router_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-router-utils title: "@kbn/router-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/router-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/router-utils'] --- import kbnRouterUtilsObj from './kbn_router_utils.devdocs.json'; diff --git a/api_docs/kbn_rrule.mdx b/api_docs/kbn_rrule.mdx index 79a6bffd7bcbc..4525652490018 100644 --- a/api_docs/kbn_rrule.mdx +++ b/api_docs/kbn_rrule.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rrule title: "@kbn/rrule" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rrule plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rrule'] --- import kbnRruleObj from './kbn_rrule.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index 9c36c38d7e6b6..56f7328d4e074 100644 --- a/api_docs/kbn_rule_data_utils.mdx +++ b/api_docs/kbn_rule_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rule-data-utils title: "@kbn/rule-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rule-data-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_utils.devdocs.json'; diff --git a/api_docs/kbn_saved_objects_settings.mdx b/api_docs/kbn_saved_objects_settings.mdx index 3a2e2eeb0b85f..b282c94a80ed1 100644 --- a/api_docs/kbn_saved_objects_settings.mdx +++ b/api_docs/kbn_saved_objects_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-saved-objects-settings title: "@kbn/saved-objects-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/saved-objects-settings plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/saved-objects-settings'] --- import kbnSavedObjectsSettingsObj from './kbn_saved_objects_settings.devdocs.json'; diff --git a/api_docs/kbn_screenshotting_server.mdx b/api_docs/kbn_screenshotting_server.mdx index d9806667eb530..e3082f75b86b5 100644 --- a/api_docs/kbn_screenshotting_server.mdx +++ b/api_docs/kbn_screenshotting_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-screenshotting-server title: "@kbn/screenshotting-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/screenshotting-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/screenshotting-server'] --- import kbnScreenshottingServerObj from './kbn_screenshotting_server.devdocs.json'; diff --git a/api_docs/kbn_search_api_keys_components.mdx b/api_docs/kbn_search_api_keys_components.mdx index 22b1edf669606..2845fb1e18728 100644 --- a/api_docs/kbn_search_api_keys_components.mdx +++ b/api_docs/kbn_search_api_keys_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-api-keys-components title: "@kbn/search-api-keys-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-api-keys-components plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-api-keys-components'] --- import kbnSearchApiKeysComponentsObj from './kbn_search_api_keys_components.devdocs.json'; diff --git a/api_docs/kbn_search_api_keys_server.mdx b/api_docs/kbn_search_api_keys_server.mdx index e9e0f52b17e12..1de1e5ce0833b 100644 --- a/api_docs/kbn_search_api_keys_server.mdx +++ b/api_docs/kbn_search_api_keys_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-api-keys-server title: "@kbn/search-api-keys-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-api-keys-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-api-keys-server'] --- import kbnSearchApiKeysServerObj from './kbn_search_api_keys_server.devdocs.json'; diff --git a/api_docs/kbn_search_api_panels.mdx b/api_docs/kbn_search_api_panels.mdx index 2005fcfa07e5f..0d8cd756524fd 100644 --- a/api_docs/kbn_search_api_panels.mdx +++ b/api_docs/kbn_search_api_panels.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-api-panels title: "@kbn/search-api-panels" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-api-panels plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-api-panels'] --- import kbnSearchApiPanelsObj from './kbn_search_api_panels.devdocs.json'; diff --git a/api_docs/kbn_search_connectors.mdx b/api_docs/kbn_search_connectors.mdx index ca16d027fa2fe..c93d99c924c28 100644 --- a/api_docs/kbn_search_connectors.mdx +++ b/api_docs/kbn_search_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-connectors title: "@kbn/search-connectors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-connectors plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-connectors'] --- import kbnSearchConnectorsObj from './kbn_search_connectors.devdocs.json'; diff --git a/api_docs/kbn_search_errors.mdx b/api_docs/kbn_search_errors.mdx index 0c4bbd3d35c09..de23e2c15d3c9 100644 --- a/api_docs/kbn_search_errors.mdx +++ b/api_docs/kbn_search_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-errors title: "@kbn/search-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-errors plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-errors'] --- import kbnSearchErrorsObj from './kbn_search_errors.devdocs.json'; diff --git a/api_docs/kbn_search_index_documents.devdocs.json b/api_docs/kbn_search_index_documents.devdocs.json index 7f2b1b151d792..47d7e742f5e90 100644 --- a/api_docs/kbn_search_index_documents.devdocs.json +++ b/api_docs/kbn_search_index_documents.devdocs.json @@ -101,7 +101,7 @@ "section": "def-server.ElasticsearchClient", "text": "ElasticsearchClient" }, - ", indexName: string, query?: string | undefined, from?: number, size?: number) => Promise<", + ", indexName: string, query?: string | undefined, from?: number, size?: number, trackTotalHits?: boolean) => Promise<", { "pluginId": "@kbn/search-index-documents", "scope": "common", @@ -197,6 +197,21 @@ "deprecated": false, "trackAdoption": false, "isRequired": true + }, + { + "parentPluginId": "@kbn/search-index-documents", + "id": "def-common.fetchSearchResults.$6", + "type": "boolean", + "tags": [], + "label": "trackTotalHits", + "description": [], + "signature": [ + "boolean" + ], + "path": "packages/kbn-search-index-documents/lib/fetch_search_results.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true } ], "returnComment": [], diff --git a/api_docs/kbn_search_index_documents.mdx b/api_docs/kbn_search_index_documents.mdx index 801433638d7f0..a2966a0a1de69 100644 --- a/api_docs/kbn_search_index_documents.mdx +++ b/api_docs/kbn_search_index_documents.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-index-documents title: "@kbn/search-index-documents" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-index-documents plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-index-documents'] --- import kbnSearchIndexDocumentsObj from './kbn_search_index_documents.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-ki | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 35 | 0 | 33 | 3 | +| 36 | 0 | 34 | 3 | ## Common diff --git a/api_docs/kbn_search_response_warnings.mdx b/api_docs/kbn_search_response_warnings.mdx index 6d3eaed18dc6f..bba562bcd6858 100644 --- a/api_docs/kbn_search_response_warnings.mdx +++ b/api_docs/kbn_search_response_warnings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-response-warnings title: "@kbn/search-response-warnings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-response-warnings plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-response-warnings'] --- import kbnSearchResponseWarningsObj from './kbn_search_response_warnings.devdocs.json'; diff --git a/api_docs/kbn_search_shared_ui.mdx b/api_docs/kbn_search_shared_ui.mdx index a9d28500d3151..c090540247403 100644 --- a/api_docs/kbn_search_shared_ui.mdx +++ b/api_docs/kbn_search_shared_ui.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-shared-ui title: "@kbn/search-shared-ui" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-shared-ui plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-shared-ui'] --- import kbnSearchSharedUiObj from './kbn_search_shared_ui.devdocs.json'; diff --git a/api_docs/kbn_search_types.mdx b/api_docs/kbn_search_types.mdx index fb5052f252819..bea8417e0a117 100644 --- a/api_docs/kbn_search_types.mdx +++ b/api_docs/kbn_search_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-types title: "@kbn/search-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-types plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-types'] --- import kbnSearchTypesObj from './kbn_search_types.devdocs.json'; diff --git a/api_docs/kbn_security_api_key_management.mdx b/api_docs/kbn_security_api_key_management.mdx index c68ff7a63c568..a407b3a320008 100644 --- a/api_docs/kbn_security_api_key_management.mdx +++ b/api_docs/kbn_security_api_key_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-api-key-management title: "@kbn/security-api-key-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-api-key-management plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-api-key-management'] --- import kbnSecurityApiKeyManagementObj from './kbn_security_api_key_management.devdocs.json'; diff --git a/api_docs/kbn_security_authorization_core.mdx b/api_docs/kbn_security_authorization_core.mdx index 202ce64e188ce..645692ea79acb 100644 --- a/api_docs/kbn_security_authorization_core.mdx +++ b/api_docs/kbn_security_authorization_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-authorization-core title: "@kbn/security-authorization-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-authorization-core plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-authorization-core'] --- import kbnSecurityAuthorizationCoreObj from './kbn_security_authorization_core.devdocs.json'; diff --git a/api_docs/kbn_security_authorization_core_common.mdx b/api_docs/kbn_security_authorization_core_common.mdx index e16536e126428..0b4d75e73399a 100644 --- a/api_docs/kbn_security_authorization_core_common.mdx +++ b/api_docs/kbn_security_authorization_core_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-authorization-core-common title: "@kbn/security-authorization-core-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-authorization-core-common plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-authorization-core-common'] --- import kbnSecurityAuthorizationCoreCommonObj from './kbn_security_authorization_core_common.devdocs.json'; diff --git a/api_docs/kbn_security_form_components.mdx b/api_docs/kbn_security_form_components.mdx index 0ee3efcc5a2ab..8df41faa758a8 100644 --- a/api_docs/kbn_security_form_components.mdx +++ b/api_docs/kbn_security_form_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-form-components title: "@kbn/security-form-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-form-components plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-form-components'] --- import kbnSecurityFormComponentsObj from './kbn_security_form_components.devdocs.json'; diff --git a/api_docs/kbn_security_hardening.mdx b/api_docs/kbn_security_hardening.mdx index 096152937178c..8a87c1292040e 100644 --- a/api_docs/kbn_security_hardening.mdx +++ b/api_docs/kbn_security_hardening.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-hardening title: "@kbn/security-hardening" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-hardening plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-hardening'] --- import kbnSecurityHardeningObj from './kbn_security_hardening.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_common.mdx b/api_docs/kbn_security_plugin_types_common.mdx index fd52206874c7b..bf3b8968baf10 100644 --- a/api_docs/kbn_security_plugin_types_common.mdx +++ b/api_docs/kbn_security_plugin_types_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-common title: "@kbn/security-plugin-types-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-common plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-common'] --- import kbnSecurityPluginTypesCommonObj from './kbn_security_plugin_types_common.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_public.mdx b/api_docs/kbn_security_plugin_types_public.mdx index 06587d3b459a1..a0d87552a9d57 100644 --- a/api_docs/kbn_security_plugin_types_public.mdx +++ b/api_docs/kbn_security_plugin_types_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-public title: "@kbn/security-plugin-types-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-public plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-public'] --- import kbnSecurityPluginTypesPublicObj from './kbn_security_plugin_types_public.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_server.mdx b/api_docs/kbn_security_plugin_types_server.mdx index 88b0fc9ee1889..4018d70c6d645 100644 --- a/api_docs/kbn_security_plugin_types_server.mdx +++ b/api_docs/kbn_security_plugin_types_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-server title: "@kbn/security-plugin-types-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-server'] --- import kbnSecurityPluginTypesServerObj from './kbn_security_plugin_types_server.devdocs.json'; diff --git a/api_docs/kbn_security_role_management_model.mdx b/api_docs/kbn_security_role_management_model.mdx index 56b66222b2ba4..b960e7eaf0587 100644 --- a/api_docs/kbn_security_role_management_model.mdx +++ b/api_docs/kbn_security_role_management_model.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-role-management-model title: "@kbn/security-role-management-model" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-role-management-model plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-role-management-model'] --- import kbnSecurityRoleManagementModelObj from './kbn_security_role_management_model.devdocs.json'; diff --git a/api_docs/kbn_security_solution_common.mdx b/api_docs/kbn_security_solution_common.mdx index 079c0ee26f771..ed71112055d49 100644 --- a/api_docs/kbn_security_solution_common.mdx +++ b/api_docs/kbn_security_solution_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-common title: "@kbn/security-solution-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-common plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-common'] --- import kbnSecuritySolutionCommonObj from './kbn_security_solution_common.devdocs.json'; diff --git a/api_docs/kbn_security_solution_distribution_bar.mdx b/api_docs/kbn_security_solution_distribution_bar.mdx index 176d78476fa8d..22f592b865a7e 100644 --- a/api_docs/kbn_security_solution_distribution_bar.mdx +++ b/api_docs/kbn_security_solution_distribution_bar.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-distribution-bar title: "@kbn/security-solution-distribution-bar" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-distribution-bar plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-distribution-bar'] --- import kbnSecuritySolutionDistributionBarObj from './kbn_security_solution_distribution_bar.devdocs.json'; diff --git a/api_docs/kbn_security_solution_features.mdx b/api_docs/kbn_security_solution_features.mdx index 0642c000d5de8..da597df7a665a 100644 --- a/api_docs/kbn_security_solution_features.mdx +++ b/api_docs/kbn_security_solution_features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-features title: "@kbn/security-solution-features" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-features plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-features'] --- import kbnSecuritySolutionFeaturesObj from './kbn_security_solution_features.devdocs.json'; diff --git a/api_docs/kbn_security_solution_navigation.mdx b/api_docs/kbn_security_solution_navigation.mdx index bb2c8dec16422..c4dd4be12b0ac 100644 --- a/api_docs/kbn_security_solution_navigation.mdx +++ b/api_docs/kbn_security_solution_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-navigation title: "@kbn/security-solution-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-navigation plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-navigation'] --- import kbnSecuritySolutionNavigationObj from './kbn_security_solution_navigation.devdocs.json'; diff --git a/api_docs/kbn_security_solution_side_nav.mdx b/api_docs/kbn_security_solution_side_nav.mdx index 69da89dd8f9fb..3ac0297baf840 100644 --- a/api_docs/kbn_security_solution_side_nav.mdx +++ b/api_docs/kbn_security_solution_side_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-side-nav title: "@kbn/security-solution-side-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-side-nav plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-side-nav'] --- import kbnSecuritySolutionSideNavObj from './kbn_security_solution_side_nav.devdocs.json'; diff --git a/api_docs/kbn_security_solution_storybook_config.mdx b/api_docs/kbn_security_solution_storybook_config.mdx index 6c2ac7352955b..2edf044b74ecc 100644 --- a/api_docs/kbn_security_solution_storybook_config.mdx +++ b/api_docs/kbn_security_solution_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-storybook-config title: "@kbn/security-solution-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-storybook-config plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-storybook-config'] --- import kbnSecuritySolutionStorybookConfigObj from './kbn_security_solution_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_security_ui_components.mdx b/api_docs/kbn_security_ui_components.mdx index 41a8b22be8bc8..395cb8ee28de8 100644 --- a/api_docs/kbn_security_ui_components.mdx +++ b/api_docs/kbn_security_ui_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-ui-components title: "@kbn/security-ui-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-ui-components plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-ui-components'] --- import kbnSecurityUiComponentsObj from './kbn_security_ui_components.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index 37c60d72d9601..5c95bdff7aa90 100644 --- a/api_docs/kbn_securitysolution_autocomplete.mdx +++ b/api_docs/kbn_securitysolution_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-autocomplete title: "@kbn/securitysolution-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-autocomplete plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] --- import kbnSecuritysolutionAutocompleteObj from './kbn_securitysolution_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_data_table.mdx b/api_docs/kbn_securitysolution_data_table.mdx index 1d70e4f561a52..bb5c5dbca6893 100644 --- a/api_docs/kbn_securitysolution_data_table.mdx +++ b/api_docs/kbn_securitysolution_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-data-table title: "@kbn/securitysolution-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-data-table plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-data-table'] --- import kbnSecuritysolutionDataTableObj from './kbn_securitysolution_data_table.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_ecs.mdx b/api_docs/kbn_securitysolution_ecs.mdx index 0ef87199a8713..541f09ae22e1c 100644 --- a/api_docs/kbn_securitysolution_ecs.mdx +++ b/api_docs/kbn_securitysolution_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-ecs title: "@kbn/securitysolution-ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-ecs plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-ecs'] --- import kbnSecuritysolutionEcsObj from './kbn_securitysolution_ecs.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_es_utils.mdx b/api_docs/kbn_securitysolution_es_utils.mdx index 7d89a5b78223b..d998e3e27889d 100644 --- a/api_docs/kbn_securitysolution_es_utils.mdx +++ b/api_docs/kbn_securitysolution_es_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-es-utils title: "@kbn/securitysolution-es-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-es-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-es-utils'] --- import kbnSecuritysolutionEsUtilsObj from './kbn_securitysolution_es_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_exception_list_components.mdx b/api_docs/kbn_securitysolution_exception_list_components.mdx index 5fb11d7ed56b4..79d7c389abff9 100644 --- a/api_docs/kbn_securitysolution_exception_list_components.mdx +++ b/api_docs/kbn_securitysolution_exception_list_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-exception-list-components title: "@kbn/securitysolution-exception-list-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-exception-list-components plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-exception-list-components'] --- import kbnSecuritysolutionExceptionListComponentsObj from './kbn_securitysolution_exception_list_components.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index 7eb29394e25ea..21e81d69d6b9b 100644 --- a/api_docs/kbn_securitysolution_hook_utils.mdx +++ b/api_docs/kbn_securitysolution_hook_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-hook-utils title: "@kbn/securitysolution-hook-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-hook-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-hook-utils'] --- import kbnSecuritysolutionHookUtilsObj from './kbn_securitysolution_hook_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx index 969c933576e3d..d9cc4737b69c3 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-alerting-types title: "@kbn/securitysolution-io-ts-alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-alerting-types plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-alerting-types'] --- import kbnSecuritysolutionIoTsAlertingTypesObj from './kbn_securitysolution_io_ts_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_list_types.mdx b/api_docs/kbn_securitysolution_io_ts_list_types.mdx index f31570fcabcd1..b05412b82e335 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_list_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-list-types title: "@kbn/securitysolution-io-ts-list-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-list-types plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-list-types'] --- import kbnSecuritysolutionIoTsListTypesObj from './kbn_securitysolution_io_ts_list_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_types.mdx b/api_docs/kbn_securitysolution_io_ts_types.mdx index 4bd911ca1d06d..d51e39b13a264 100644 --- a/api_docs/kbn_securitysolution_io_ts_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-types title: "@kbn/securitysolution-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-types plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-types'] --- import kbnSecuritysolutionIoTsTypesObj from './kbn_securitysolution_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_utils.mdx b/api_docs/kbn_securitysolution_io_ts_utils.mdx index 79c521a9a1d83..d915a9b69e451 100644 --- a/api_docs/kbn_securitysolution_io_ts_utils.mdx +++ b/api_docs/kbn_securitysolution_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-utils title: "@kbn/securitysolution-io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-utils'] --- import kbnSecuritysolutionIoTsUtilsObj from './kbn_securitysolution_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_api.mdx b/api_docs/kbn_securitysolution_list_api.mdx index d3a48004bb32e..6319a128d2614 100644 --- a/api_docs/kbn_securitysolution_list_api.mdx +++ b/api_docs/kbn_securitysolution_list_api.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-api title: "@kbn/securitysolution-list-api" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-api plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-api'] --- import kbnSecuritysolutionListApiObj from './kbn_securitysolution_list_api.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_constants.mdx b/api_docs/kbn_securitysolution_list_constants.mdx index 149a19435574f..347e4fac556c2 100644 --- a/api_docs/kbn_securitysolution_list_constants.mdx +++ b/api_docs/kbn_securitysolution_list_constants.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-constants title: "@kbn/securitysolution-list-constants" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-constants plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-constants'] --- import kbnSecuritysolutionListConstantsObj from './kbn_securitysolution_list_constants.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_hooks.mdx b/api_docs/kbn_securitysolution_list_hooks.mdx index 373e6f05172b4..a7ea5658579a9 100644 --- a/api_docs/kbn_securitysolution_list_hooks.mdx +++ b/api_docs/kbn_securitysolution_list_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-hooks title: "@kbn/securitysolution-list-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-hooks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-hooks'] --- import kbnSecuritysolutionListHooksObj from './kbn_securitysolution_list_hooks.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_utils.mdx b/api_docs/kbn_securitysolution_list_utils.mdx index cb23788e8e475..eedf19f5a2fa6 100644 --- a/api_docs/kbn_securitysolution_list_utils.mdx +++ b/api_docs/kbn_securitysolution_list_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-utils title: "@kbn/securitysolution-list-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-utils'] --- import kbnSecuritysolutionListUtilsObj from './kbn_securitysolution_list_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_rules.mdx b/api_docs/kbn_securitysolution_rules.mdx index 6764a0ab1c241..4d87ee04c6f58 100644 --- a/api_docs/kbn_securitysolution_rules.mdx +++ b/api_docs/kbn_securitysolution_rules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-rules title: "@kbn/securitysolution-rules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-rules plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-rules'] --- import kbnSecuritysolutionRulesObj from './kbn_securitysolution_rules.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_t_grid.mdx b/api_docs/kbn_securitysolution_t_grid.mdx index 6cc1522c3d5ee..02c105f935d4b 100644 --- a/api_docs/kbn_securitysolution_t_grid.mdx +++ b/api_docs/kbn_securitysolution_t_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-t-grid title: "@kbn/securitysolution-t-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-t-grid plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-t-grid'] --- import kbnSecuritysolutionTGridObj from './kbn_securitysolution_t_grid.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_utils.mdx b/api_docs/kbn_securitysolution_utils.mdx index d8a4331f69020..7a979fbfb7d61 100644 --- a/api_docs/kbn_securitysolution_utils.mdx +++ b/api_docs/kbn_securitysolution_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-utils title: "@kbn/securitysolution-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-utils'] --- import kbnSecuritysolutionUtilsObj from './kbn_securitysolution_utils.devdocs.json'; diff --git a/api_docs/kbn_server_http_tools.mdx b/api_docs/kbn_server_http_tools.mdx index 8967529d7a4c7..9d3397d0fe297 100644 --- a/api_docs/kbn_server_http_tools.mdx +++ b/api_docs/kbn_server_http_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-http-tools title: "@kbn/server-http-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-http-tools plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-http-tools'] --- import kbnServerHttpToolsObj from './kbn_server_http_tools.devdocs.json'; diff --git a/api_docs/kbn_server_route_repository.mdx b/api_docs/kbn_server_route_repository.mdx index 39063e0a73894..f02ba4e265299 100644 --- a/api_docs/kbn_server_route_repository.mdx +++ b/api_docs/kbn_server_route_repository.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository title: "@kbn/server-route-repository" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_server_route_repository_client.mdx b/api_docs/kbn_server_route_repository_client.mdx index 3a7fddb14d695..2acbfa062be9c 100644 --- a/api_docs/kbn_server_route_repository_client.mdx +++ b/api_docs/kbn_server_route_repository_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository-client title: "@kbn/server-route-repository-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository-client plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository-client'] --- import kbnServerRouteRepositoryClientObj from './kbn_server_route_repository_client.devdocs.json'; diff --git a/api_docs/kbn_server_route_repository_utils.mdx b/api_docs/kbn_server_route_repository_utils.mdx index bc93c8435ff96..f97dff4c17dbf 100644 --- a/api_docs/kbn_server_route_repository_utils.mdx +++ b/api_docs/kbn_server_route_repository_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository-utils title: "@kbn/server-route-repository-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository-utils'] --- import kbnServerRouteRepositoryUtilsObj from './kbn_server_route_repository_utils.devdocs.json'; diff --git a/api_docs/kbn_serverless_common_settings.mdx b/api_docs/kbn_serverless_common_settings.mdx index 42e617a9a162b..f10ab15b9f5d0 100644 --- a/api_docs/kbn_serverless_common_settings.mdx +++ b/api_docs/kbn_serverless_common_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-common-settings title: "@kbn/serverless-common-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-common-settings plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-common-settings'] --- import kbnServerlessCommonSettingsObj from './kbn_serverless_common_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_observability_settings.mdx b/api_docs/kbn_serverless_observability_settings.mdx index f0df80fa43865..fcdebd2eebe2c 100644 --- a/api_docs/kbn_serverless_observability_settings.mdx +++ b/api_docs/kbn_serverless_observability_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-observability-settings title: "@kbn/serverless-observability-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-observability-settings plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-observability-settings'] --- import kbnServerlessObservabilitySettingsObj from './kbn_serverless_observability_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_project_switcher.mdx b/api_docs/kbn_serverless_project_switcher.mdx index 0226d50414781..8f391f3544136 100644 --- a/api_docs/kbn_serverless_project_switcher.mdx +++ b/api_docs/kbn_serverless_project_switcher.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-project-switcher title: "@kbn/serverless-project-switcher" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-project-switcher plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-project-switcher'] --- import kbnServerlessProjectSwitcherObj from './kbn_serverless_project_switcher.devdocs.json'; diff --git a/api_docs/kbn_serverless_search_settings.mdx b/api_docs/kbn_serverless_search_settings.mdx index b15a59e5bbcb8..b5c1cc82e5ac5 100644 --- a/api_docs/kbn_serverless_search_settings.mdx +++ b/api_docs/kbn_serverless_search_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-search-settings title: "@kbn/serverless-search-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-search-settings plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-search-settings'] --- import kbnServerlessSearchSettingsObj from './kbn_serverless_search_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_security_settings.mdx b/api_docs/kbn_serverless_security_settings.mdx index c0f0d1596fde2..9f32d4fc2f8e0 100644 --- a/api_docs/kbn_serverless_security_settings.mdx +++ b/api_docs/kbn_serverless_security_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-security-settings title: "@kbn/serverless-security-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-security-settings plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-security-settings'] --- import kbnServerlessSecuritySettingsObj from './kbn_serverless_security_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_storybook_config.mdx b/api_docs/kbn_serverless_storybook_config.mdx index d7b4c34e4d626..8a36d8e0c5661 100644 --- a/api_docs/kbn_serverless_storybook_config.mdx +++ b/api_docs/kbn_serverless_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-storybook-config title: "@kbn/serverless-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-storybook-config plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-storybook-config'] --- import kbnServerlessStorybookConfigObj from './kbn_serverless_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx index ae5619327957f..d4f4af2f2da48 100644 --- a/api_docs/kbn_shared_svg.mdx +++ b/api_docs/kbn_shared_svg.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-svg title: "@kbn/shared-svg" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-svg plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-svg'] --- import kbnSharedSvgObj from './kbn_shared_svg.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_avatar_solution.mdx b/api_docs/kbn_shared_ux_avatar_solution.mdx index 891fd164ef1b4..50e6a08b4ade4 100644 --- a/api_docs/kbn_shared_ux_avatar_solution.mdx +++ b/api_docs/kbn_shared_ux_avatar_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-solution title: "@kbn/shared-ux-avatar-solution" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-solution plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-solution'] --- import kbnSharedUxAvatarSolutionObj from './kbn_shared_ux_avatar_solution.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx index 3bf372b0e8866..feacdc2632c64 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen title: "@kbn/shared-ux-button-exit-full-screen" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen'] --- import kbnSharedUxButtonExitFullScreenObj from './kbn_shared_ux_button_exit_full_screen.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index 2bb28c62fc994..5c03b638fd399 100644 --- a/api_docs/kbn_shared_ux_button_toolbar.mdx +++ b/api_docs/kbn_shared_ux_button_toolbar.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-toolbar title: "@kbn/shared-ux-button-toolbar" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-toolbar plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-toolbar'] --- import kbnSharedUxButtonToolbarObj from './kbn_shared_ux_button_toolbar.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data.mdx b/api_docs/kbn_shared_ux_card_no_data.mdx index 465e386f38b66..2cb58f6b51a91 100644 --- a/api_docs/kbn_shared_ux_card_no_data.mdx +++ b/api_docs/kbn_shared_ux_card_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data title: "@kbn/shared-ux-card-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data'] --- import kbnSharedUxCardNoDataObj from './kbn_shared_ux_card_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx index 7faa4ddeeeced..2016787a52e26 100644 --- a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data-mocks title: "@kbn/shared-ux-card-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data-mocks'] --- import kbnSharedUxCardNoDataMocksObj from './kbn_shared_ux_card_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_chrome_navigation.mdx b/api_docs/kbn_shared_ux_chrome_navigation.mdx index 52e293ca6a37f..7e2b6b8278fe3 100644 --- a/api_docs/kbn_shared_ux_chrome_navigation.mdx +++ b/api_docs/kbn_shared_ux_chrome_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-chrome-navigation title: "@kbn/shared-ux-chrome-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-chrome-navigation plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-chrome-navigation'] --- import kbnSharedUxChromeNavigationObj from './kbn_shared_ux_chrome_navigation.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_error_boundary.mdx b/api_docs/kbn_shared_ux_error_boundary.mdx index 5554e0a3abbb4..0c6210a159c78 100644 --- a/api_docs/kbn_shared_ux_error_boundary.mdx +++ b/api_docs/kbn_shared_ux_error_boundary.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-error-boundary title: "@kbn/shared-ux-error-boundary" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-error-boundary plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-error-boundary'] --- import kbnSharedUxErrorBoundaryObj from './kbn_shared_ux_error_boundary.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_context.mdx b/api_docs/kbn_shared_ux_file_context.mdx index 804e67ed804e6..d637a3f43b5a2 100644 --- a/api_docs/kbn_shared_ux_file_context.mdx +++ b/api_docs/kbn_shared_ux_file_context.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-context title: "@kbn/shared-ux-file-context" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-context plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-context'] --- import kbnSharedUxFileContextObj from './kbn_shared_ux_file_context.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image.mdx b/api_docs/kbn_shared_ux_file_image.mdx index 7b8c0acdf47bc..52de3be77d0f1 100644 --- a/api_docs/kbn_shared_ux_file_image.mdx +++ b/api_docs/kbn_shared_ux_file_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image title: "@kbn/shared-ux-file-image" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image'] --- import kbnSharedUxFileImageObj from './kbn_shared_ux_file_image.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image_mocks.mdx b/api_docs/kbn_shared_ux_file_image_mocks.mdx index b639f7282ca2f..c4f449d777950 100644 --- a/api_docs/kbn_shared_ux_file_image_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_image_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image-mocks title: "@kbn/shared-ux-file-image-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image-mocks'] --- import kbnSharedUxFileImageMocksObj from './kbn_shared_ux_file_image_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_mocks.mdx b/api_docs/kbn_shared_ux_file_mocks.mdx index b7a756be76f3b..2e378c4b106d1 100644 --- a/api_docs/kbn_shared_ux_file_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-mocks title: "@kbn/shared-ux-file-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-mocks'] --- import kbnSharedUxFileMocksObj from './kbn_shared_ux_file_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_picker.mdx b/api_docs/kbn_shared_ux_file_picker.mdx index 61a6f64d470f2..5bff7c819d44e 100644 --- a/api_docs/kbn_shared_ux_file_picker.mdx +++ b/api_docs/kbn_shared_ux_file_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-picker title: "@kbn/shared-ux-file-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-picker plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-picker'] --- import kbnSharedUxFilePickerObj from './kbn_shared_ux_file_picker.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_types.mdx b/api_docs/kbn_shared_ux_file_types.mdx index e9b4ddcac27e7..1ea10c9a51389 100644 --- a/api_docs/kbn_shared_ux_file_types.mdx +++ b/api_docs/kbn_shared_ux_file_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-types title: "@kbn/shared-ux-file-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-types plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-types'] --- import kbnSharedUxFileTypesObj from './kbn_shared_ux_file_types.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_upload.mdx b/api_docs/kbn_shared_ux_file_upload.mdx index a6ec1fc9c05d4..5fd9689dac140 100644 --- a/api_docs/kbn_shared_ux_file_upload.mdx +++ b/api_docs/kbn_shared_ux_file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-upload title: "@kbn/shared-ux-file-upload" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-upload plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-upload'] --- import kbnSharedUxFileUploadObj from './kbn_shared_ux_file_upload.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_util.mdx b/api_docs/kbn_shared_ux_file_util.mdx index 35f14f63ff88c..7c2f668bda7b5 100644 --- a/api_docs/kbn_shared_ux_file_util.mdx +++ b/api_docs/kbn_shared_ux_file_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-util title: "@kbn/shared-ux-file-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-util plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-util'] --- import kbnSharedUxFileUtilObj from './kbn_shared_ux_file_util.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app.mdx b/api_docs/kbn_shared_ux_link_redirect_app.mdx index ddf57192d3294..e577b4e51f263 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app title: "@kbn/shared-ux-link-redirect-app" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app'] --- import kbnSharedUxLinkRedirectAppObj from './kbn_shared_ux_link_redirect_app.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx index 21b7e2474a0a1..85ffed06259b1 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app-mocks title: "@kbn/shared-ux-link-redirect-app-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app-mocks'] --- import kbnSharedUxLinkRedirectAppMocksObj from './kbn_shared_ux_link_redirect_app_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown.mdx b/api_docs/kbn_shared_ux_markdown.mdx index 295084ba7c371..249a01345a304 100644 --- a/api_docs/kbn_shared_ux_markdown.mdx +++ b/api_docs/kbn_shared_ux_markdown.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown title: "@kbn/shared-ux-markdown" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown'] --- import kbnSharedUxMarkdownObj from './kbn_shared_ux_markdown.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown_mocks.mdx b/api_docs/kbn_shared_ux_markdown_mocks.mdx index d8b44f41bb407..64676d962241e 100644 --- a/api_docs/kbn_shared_ux_markdown_mocks.mdx +++ b/api_docs/kbn_shared_ux_markdown_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown-mocks title: "@kbn/shared-ux-markdown-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown-mocks'] --- import kbnSharedUxMarkdownMocksObj from './kbn_shared_ux_markdown_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx index 6d3fbabd9456b..3d64299f00a30 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data title: "@kbn/shared-ux-page-analytics-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data'] --- import kbnSharedUxPageAnalyticsNoDataObj from './kbn_shared_ux_page_analytics_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx index 92ff26f23f95c..51ca52e3ffb19 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data-mocks title: "@kbn/shared-ux-page-analytics-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data-mocks'] --- import kbnSharedUxPageAnalyticsNoDataMocksObj from './kbn_shared_ux_page_analytics_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx index 472c70a3149eb..9dcb53ba039ab 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data title: "@kbn/shared-ux-page-kibana-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data'] --- import kbnSharedUxPageKibanaNoDataObj from './kbn_shared_ux_page_kibana_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx index 04da3794575fb..e2e9c1add3798 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data-mocks title: "@kbn/shared-ux-page-kibana-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data-mocks'] --- import kbnSharedUxPageKibanaNoDataMocksObj from './kbn_shared_ux_page_kibana_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template.mdx b/api_docs/kbn_shared_ux_page_kibana_template.mdx index 57e118cf4e90e..b1dc8932eacb0 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template title: "@kbn/shared-ux-page-kibana-template" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template'] --- import kbnSharedUxPageKibanaTemplateObj from './kbn_shared_ux_page_kibana_template.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx index 146ef0588c05d..93abefa86666b 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template-mocks title: "@kbn/shared-ux-page-kibana-template-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template-mocks'] --- import kbnSharedUxPageKibanaTemplateMocksObj from './kbn_shared_ux_page_kibana_template_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data.mdx b/api_docs/kbn_shared_ux_page_no_data.mdx index f9b19d5b37804..57577e25afd73 100644 --- a/api_docs/kbn_shared_ux_page_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data title: "@kbn/shared-ux-page-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data'] --- import kbnSharedUxPageNoDataObj from './kbn_shared_ux_page_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config.mdx b/api_docs/kbn_shared_ux_page_no_data_config.mdx index 5ee26e3ad4d2f..9cf3ebda4ecdc 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config title: "@kbn/shared-ux-page-no-data-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config'] --- import kbnSharedUxPageNoDataConfigObj from './kbn_shared_ux_page_no_data_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx index 07c5cf235883f..af6c8529e934d 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config-mocks title: "@kbn/shared-ux-page-no-data-config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config-mocks'] --- import kbnSharedUxPageNoDataConfigMocksObj from './kbn_shared_ux_page_no_data_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx index c8740f7521898..bd9d5a5208973 100644 --- a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-mocks title: "@kbn/shared-ux-page-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-mocks'] --- import kbnSharedUxPageNoDataMocksObj from './kbn_shared_ux_page_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_solution_nav.mdx b/api_docs/kbn_shared_ux_page_solution_nav.mdx index 95489e48b05b1..c9597f8c72bc3 100644 --- a/api_docs/kbn_shared_ux_page_solution_nav.mdx +++ b/api_docs/kbn_shared_ux_page_solution_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-solution-nav title: "@kbn/shared-ux-page-solution-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-solution-nav plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-solution-nav'] --- import kbnSharedUxPageSolutionNavObj from './kbn_shared_ux_page_solution_nav.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx index a66475b3ccb67..3096f8a41022e 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views title: "@kbn/shared-ux-prompt-no-data-views" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views'] --- import kbnSharedUxPromptNoDataViewsObj from './kbn_shared_ux_prompt_no_data_views.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx index 096967bef611a..745031615d988 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views-mocks title: "@kbn/shared-ux-prompt-no-data-views-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views-mocks'] --- import kbnSharedUxPromptNoDataViewsMocksObj from './kbn_shared_ux_prompt_no_data_views_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_not_found.mdx b/api_docs/kbn_shared_ux_prompt_not_found.mdx index 42ec2a5753d12..444e65400d4f7 100644 --- a/api_docs/kbn_shared_ux_prompt_not_found.mdx +++ b/api_docs/kbn_shared_ux_prompt_not_found.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-not-found title: "@kbn/shared-ux-prompt-not-found" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-not-found plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-not-found'] --- import kbnSharedUxPromptNotFoundObj from './kbn_shared_ux_prompt_not_found.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router.mdx b/api_docs/kbn_shared_ux_router.mdx index af860f9661866..10985ea8ec3f5 100644 --- a/api_docs/kbn_shared_ux_router.mdx +++ b/api_docs/kbn_shared_ux_router.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router title: "@kbn/shared-ux-router" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router'] --- import kbnSharedUxRouterObj from './kbn_shared_ux_router.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router_mocks.mdx b/api_docs/kbn_shared_ux_router_mocks.mdx index d9cf7a3765bd5..eba8d5338ce9c 100644 --- a/api_docs/kbn_shared_ux_router_mocks.mdx +++ b/api_docs/kbn_shared_ux_router_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router-mocks title: "@kbn/shared-ux-router-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router-mocks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router-mocks'] --- import kbnSharedUxRouterMocksObj from './kbn_shared_ux_router_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_config.mdx b/api_docs/kbn_shared_ux_storybook_config.mdx index 8de1bda3ac22e..ee2c8ffcf6e6d 100644 --- a/api_docs/kbn_shared_ux_storybook_config.mdx +++ b/api_docs/kbn_shared_ux_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-config title: "@kbn/shared-ux-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-config plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-config'] --- import kbnSharedUxStorybookConfigObj from './kbn_shared_ux_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_mock.mdx b/api_docs/kbn_shared_ux_storybook_mock.mdx index 5758c50422f1e..3ec1c45272058 100644 --- a/api_docs/kbn_shared_ux_storybook_mock.mdx +++ b/api_docs/kbn_shared_ux_storybook_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-mock title: "@kbn/shared-ux-storybook-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-mock plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-mock'] --- import kbnSharedUxStorybookMockObj from './kbn_shared_ux_storybook_mock.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_tabbed_modal.mdx b/api_docs/kbn_shared_ux_tabbed_modal.mdx index a724852b1e966..c0b05ef26e750 100644 --- a/api_docs/kbn_shared_ux_tabbed_modal.mdx +++ b/api_docs/kbn_shared_ux_tabbed_modal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-tabbed-modal title: "@kbn/shared-ux-tabbed-modal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-tabbed-modal plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-tabbed-modal'] --- import kbnSharedUxTabbedModalObj from './kbn_shared_ux_tabbed_modal.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_table_persist.mdx b/api_docs/kbn_shared_ux_table_persist.mdx index 040195f4d5b06..d85a2f11f18dd 100644 --- a/api_docs/kbn_shared_ux_table_persist.mdx +++ b/api_docs/kbn_shared_ux_table_persist.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-table-persist title: "@kbn/shared-ux-table-persist" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-table-persist plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-table-persist'] --- import kbnSharedUxTablePersistObj from './kbn_shared_ux_table_persist.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index 3f0964af3ac0f..5b05d2802688f 100644 --- a/api_docs/kbn_shared_ux_utility.mdx +++ b/api_docs/kbn_shared_ux_utility.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-utility title: "@kbn/shared-ux-utility" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-utility plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_slo_schema.mdx b/api_docs/kbn_slo_schema.mdx index 9a9af1348f9bc..2d9c8b39007f5 100644 --- a/api_docs/kbn_slo_schema.mdx +++ b/api_docs/kbn_slo_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-slo-schema title: "@kbn/slo-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/slo-schema plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/slo-schema'] --- import kbnSloSchemaObj from './kbn_slo_schema.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index 6f9b445dfac41..72554d86c0120 100644 --- a/api_docs/kbn_some_dev_log.mdx +++ b/api_docs/kbn_some_dev_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-some-dev-log title: "@kbn/some-dev-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/some-dev-log plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/some-dev-log'] --- import kbnSomeDevLogObj from './kbn_some_dev_log.devdocs.json'; diff --git a/api_docs/kbn_sort_predicates.mdx b/api_docs/kbn_sort_predicates.mdx index 77d3f4c1e7415..f7d01de3eaca0 100644 --- a/api_docs/kbn_sort_predicates.mdx +++ b/api_docs/kbn_sort_predicates.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sort-predicates title: "@kbn/sort-predicates" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sort-predicates plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sort-predicates'] --- import kbnSortPredicatesObj from './kbn_sort_predicates.devdocs.json'; diff --git a/api_docs/kbn_sse_utils.mdx b/api_docs/kbn_sse_utils.mdx index ec00c4c3cde2e..390356a88c393 100644 --- a/api_docs/kbn_sse_utils.mdx +++ b/api_docs/kbn_sse_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sse-utils title: "@kbn/sse-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sse-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sse-utils'] --- import kbnSseUtilsObj from './kbn_sse_utils.devdocs.json'; diff --git a/api_docs/kbn_sse_utils_client.mdx b/api_docs/kbn_sse_utils_client.mdx index 08ee632b8860f..dc062aec05a15 100644 --- a/api_docs/kbn_sse_utils_client.mdx +++ b/api_docs/kbn_sse_utils_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sse-utils-client title: "@kbn/sse-utils-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sse-utils-client plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sse-utils-client'] --- import kbnSseUtilsClientObj from './kbn_sse_utils_client.devdocs.json'; diff --git a/api_docs/kbn_sse_utils_server.mdx b/api_docs/kbn_sse_utils_server.mdx index 62c0308da36ff..cb21174d88223 100644 --- a/api_docs/kbn_sse_utils_server.mdx +++ b/api_docs/kbn_sse_utils_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sse-utils-server title: "@kbn/sse-utils-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sse-utils-server plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sse-utils-server'] --- import kbnSseUtilsServerObj from './kbn_sse_utils_server.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index 9e462ca2ce9d1..c91da1c57f34b 100644 --- a/api_docs/kbn_std.mdx +++ b/api_docs/kbn_std.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-std title: "@kbn/std" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/std plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/std'] --- import kbnStdObj from './kbn_std.devdocs.json'; diff --git a/api_docs/kbn_stdio_dev_helpers.mdx b/api_docs/kbn_stdio_dev_helpers.mdx index bf0be9c4c3e75..d3afb906db3ea 100644 --- a/api_docs/kbn_stdio_dev_helpers.mdx +++ b/api_docs/kbn_stdio_dev_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-stdio-dev-helpers title: "@kbn/stdio-dev-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/stdio-dev-helpers plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/stdio-dev-helpers'] --- import kbnStdioDevHelpersObj from './kbn_stdio_dev_helpers.devdocs.json'; diff --git a/api_docs/kbn_storybook.mdx b/api_docs/kbn_storybook.mdx index e3a1e9e435b65..4cc9d348971e5 100644 --- a/api_docs/kbn_storybook.mdx +++ b/api_docs/kbn_storybook.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-storybook title: "@kbn/storybook" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/storybook plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] --- import kbnStorybookObj from './kbn_storybook.devdocs.json'; diff --git a/api_docs/kbn_synthetics_e2e.mdx b/api_docs/kbn_synthetics_e2e.mdx index 7401ae622c19b..6650bf1e9a14d 100644 --- a/api_docs/kbn_synthetics_e2e.mdx +++ b/api_docs/kbn_synthetics_e2e.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-synthetics-e2e title: "@kbn/synthetics-e2e" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/synthetics-e2e plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/synthetics-e2e'] --- import kbnSyntheticsE2eObj from './kbn_synthetics_e2e.devdocs.json'; diff --git a/api_docs/kbn_synthetics_private_location.mdx b/api_docs/kbn_synthetics_private_location.mdx index aa9004a309717..5b14270220c3e 100644 --- a/api_docs/kbn_synthetics_private_location.mdx +++ b/api_docs/kbn_synthetics_private_location.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-synthetics-private-location title: "@kbn/synthetics-private-location" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/synthetics-private-location plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/synthetics-private-location'] --- import kbnSyntheticsPrivateLocationObj from './kbn_synthetics_private_location.devdocs.json'; diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index 53cd2b4247ef3..2b8aedeb17cc7 100644 --- a/api_docs/kbn_telemetry_tools.mdx +++ b/api_docs/kbn_telemetry_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-telemetry-tools title: "@kbn/telemetry-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/telemetry-tools plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/telemetry-tools'] --- import kbnTelemetryToolsObj from './kbn_telemetry_tools.devdocs.json'; diff --git a/api_docs/kbn_test.mdx b/api_docs/kbn_test.mdx index 679b82a427a2c..3c00cb23fd793 100644 --- a/api_docs/kbn_test.mdx +++ b/api_docs/kbn_test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test title: "@kbn/test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; diff --git a/api_docs/kbn_test_eui_helpers.mdx b/api_docs/kbn_test_eui_helpers.mdx index 36e464722e393..ad306a1a08b30 100644 --- a/api_docs/kbn_test_eui_helpers.mdx +++ b/api_docs/kbn_test_eui_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-eui-helpers title: "@kbn/test-eui-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-eui-helpers plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-eui-helpers'] --- import kbnTestEuiHelpersObj from './kbn_test_eui_helpers.devdocs.json'; diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index fcb29e10a7359..520050e16b6fe 100644 --- a/api_docs/kbn_test_jest_helpers.mdx +++ b/api_docs/kbn_test_jest_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-jest-helpers title: "@kbn/test-jest-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-jest-helpers plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-jest-helpers'] --- import kbnTestJestHelpersObj from './kbn_test_jest_helpers.devdocs.json'; diff --git a/api_docs/kbn_test_subj_selector.mdx b/api_docs/kbn_test_subj_selector.mdx index fecd44e5f7f18..24627dfba473c 100644 --- a/api_docs/kbn_test_subj_selector.mdx +++ b/api_docs/kbn_test_subj_selector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-subj-selector title: "@kbn/test-subj-selector" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-subj-selector plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_timerange.mdx b/api_docs/kbn_timerange.mdx index 79099c9d0a949..994c0cff4a192 100644 --- a/api_docs/kbn_timerange.mdx +++ b/api_docs/kbn_timerange.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-timerange title: "@kbn/timerange" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/timerange plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/timerange'] --- import kbnTimerangeObj from './kbn_timerange.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index cd89ef3b56124..75a98aff73f2a 100644 --- a/api_docs/kbn_tooling_log.mdx +++ b/api_docs/kbn_tooling_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-tooling-log title: "@kbn/tooling-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/tooling-log plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_triggers_actions_ui_types.mdx b/api_docs/kbn_triggers_actions_ui_types.mdx index 2fa900f795f46..c65634c3cdeeb 100644 --- a/api_docs/kbn_triggers_actions_ui_types.mdx +++ b/api_docs/kbn_triggers_actions_ui_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-triggers-actions-ui-types title: "@kbn/triggers-actions-ui-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/triggers-actions-ui-types plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/triggers-actions-ui-types'] --- import kbnTriggersActionsUiTypesObj from './kbn_triggers_actions_ui_types.devdocs.json'; diff --git a/api_docs/kbn_try_in_console.mdx b/api_docs/kbn_try_in_console.mdx index c298693bda248..9e8586522336c 100644 --- a/api_docs/kbn_try_in_console.mdx +++ b/api_docs/kbn_try_in_console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-try-in-console title: "@kbn/try-in-console" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/try-in-console plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/try-in-console'] --- import kbnTryInConsoleObj from './kbn_try_in_console.devdocs.json'; diff --git a/api_docs/kbn_ts_projects.mdx b/api_docs/kbn_ts_projects.mdx index 8b363c6a96656..b57f4230e3f0c 100644 --- a/api_docs/kbn_ts_projects.mdx +++ b/api_docs/kbn_ts_projects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ts-projects title: "@kbn/ts-projects" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ts-projects plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ts-projects'] --- import kbnTsProjectsObj from './kbn_ts_projects.devdocs.json'; diff --git a/api_docs/kbn_typed_react_router_config.mdx b/api_docs/kbn_typed_react_router_config.mdx index af04d49c66481..83133ef76f906 100644 --- a/api_docs/kbn_typed_react_router_config.mdx +++ b/api_docs/kbn_typed_react_router_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-typed-react-router-config title: "@kbn/typed-react-router-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/typed-react-router-config plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/typed-react-router-config'] --- import kbnTypedReactRouterConfigObj from './kbn_typed_react_router_config.devdocs.json'; diff --git a/api_docs/kbn_ui_actions_browser.mdx b/api_docs/kbn_ui_actions_browser.mdx index 288360145cd5d..867d3998e892f 100644 --- a/api_docs/kbn_ui_actions_browser.mdx +++ b/api_docs/kbn_ui_actions_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-actions-browser title: "@kbn/ui-actions-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-actions-browser plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-actions-browser'] --- import kbnUiActionsBrowserObj from './kbn_ui_actions_browser.devdocs.json'; diff --git a/api_docs/kbn_ui_shared_deps_src.mdx b/api_docs/kbn_ui_shared_deps_src.mdx index df6ebe1236357..0d9ddbfcde4cb 100644 --- a/api_docs/kbn_ui_shared_deps_src.mdx +++ b/api_docs/kbn_ui_shared_deps_src.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-shared-deps-src title: "@kbn/ui-shared-deps-src" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-shared-deps-src plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-shared-deps-src'] --- import kbnUiSharedDepsSrcObj from './kbn_ui_shared_deps_src.devdocs.json'; diff --git a/api_docs/kbn_ui_theme.mdx b/api_docs/kbn_ui_theme.mdx index 01b5cd64d993b..d1576c1df19c3 100644 --- a/api_docs/kbn_ui_theme.mdx +++ b/api_docs/kbn_ui_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-theme title: "@kbn/ui-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-theme plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_unified_data_table.mdx b/api_docs/kbn_unified_data_table.mdx index 3261444074f5d..4818f43faf153 100644 --- a/api_docs/kbn_unified_data_table.mdx +++ b/api_docs/kbn_unified_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-data-table title: "@kbn/unified-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-data-table plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-data-table'] --- import kbnUnifiedDataTableObj from './kbn_unified_data_table.devdocs.json'; diff --git a/api_docs/kbn_unified_doc_viewer.mdx b/api_docs/kbn_unified_doc_viewer.mdx index 4af11eee1c37b..f276e041a5f13 100644 --- a/api_docs/kbn_unified_doc_viewer.mdx +++ b/api_docs/kbn_unified_doc_viewer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-doc-viewer title: "@kbn/unified-doc-viewer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-doc-viewer plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-doc-viewer'] --- import kbnUnifiedDocViewerObj from './kbn_unified_doc_viewer.devdocs.json'; diff --git a/api_docs/kbn_unified_field_list.mdx b/api_docs/kbn_unified_field_list.mdx index b83bf7c3f8cd8..b863d2fb06c57 100644 --- a/api_docs/kbn_unified_field_list.mdx +++ b/api_docs/kbn_unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-field-list title: "@kbn/unified-field-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-field-list plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-field-list'] --- import kbnUnifiedFieldListObj from './kbn_unified_field_list.devdocs.json'; diff --git a/api_docs/kbn_unsaved_changes_badge.mdx b/api_docs/kbn_unsaved_changes_badge.mdx index e8f7849c2e142..df6802ed7e2e1 100644 --- a/api_docs/kbn_unsaved_changes_badge.mdx +++ b/api_docs/kbn_unsaved_changes_badge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unsaved-changes-badge title: "@kbn/unsaved-changes-badge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unsaved-changes-badge plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unsaved-changes-badge'] --- import kbnUnsavedChangesBadgeObj from './kbn_unsaved_changes_badge.devdocs.json'; diff --git a/api_docs/kbn_unsaved_changes_prompt.mdx b/api_docs/kbn_unsaved_changes_prompt.mdx index b887e0abc01f2..c6d34ba101195 100644 --- a/api_docs/kbn_unsaved_changes_prompt.mdx +++ b/api_docs/kbn_unsaved_changes_prompt.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unsaved-changes-prompt title: "@kbn/unsaved-changes-prompt" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unsaved-changes-prompt plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unsaved-changes-prompt'] --- import kbnUnsavedChangesPromptObj from './kbn_unsaved_changes_prompt.devdocs.json'; diff --git a/api_docs/kbn_use_tracked_promise.mdx b/api_docs/kbn_use_tracked_promise.mdx index d61d6b4e50b4e..fa4c695d8645d 100644 --- a/api_docs/kbn_use_tracked_promise.mdx +++ b/api_docs/kbn_use_tracked_promise.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-use-tracked-promise title: "@kbn/use-tracked-promise" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/use-tracked-promise plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/use-tracked-promise'] --- import kbnUseTrackedPromiseObj from './kbn_use_tracked_promise.devdocs.json'; diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index 310ade56df362..d66a002bb400a 100644 --- a/api_docs/kbn_user_profile_components.mdx +++ b/api_docs/kbn_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-user-profile-components title: "@kbn/user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/user-profile-components plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/user-profile-components'] --- import kbnUserProfileComponentsObj from './kbn_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_utility_types.mdx b/api_docs/kbn_utility_types.mdx index cbac4b5125b78..0a77846295acc 100644 --- a/api_docs/kbn_utility_types.mdx +++ b/api_docs/kbn_utility_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types title: "@kbn/utility-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types'] --- import kbnUtilityTypesObj from './kbn_utility_types.devdocs.json'; diff --git a/api_docs/kbn_utility_types_jest.mdx b/api_docs/kbn_utility_types_jest.mdx index fe16d03753fd2..264f8539a7b71 100644 --- a/api_docs/kbn_utility_types_jest.mdx +++ b/api_docs/kbn_utility_types_jest.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types-jest title: "@kbn/utility-types-jest" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types-jest plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types-jest'] --- import kbnUtilityTypesJestObj from './kbn_utility_types_jest.devdocs.json'; diff --git a/api_docs/kbn_utils.mdx b/api_docs/kbn_utils.mdx index eab246b8d748b..532f7e5b523e6 100644 --- a/api_docs/kbn_utils.mdx +++ b/api_docs/kbn_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utils title: "@kbn/utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_visualization_ui_components.mdx b/api_docs/kbn_visualization_ui_components.mdx index 2bcf66ab2fc8e..7c896ed630cdb 100644 --- a/api_docs/kbn_visualization_ui_components.mdx +++ b/api_docs/kbn_visualization_ui_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-visualization-ui-components title: "@kbn/visualization-ui-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/visualization-ui-components plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/visualization-ui-components'] --- import kbnVisualizationUiComponentsObj from './kbn_visualization_ui_components.devdocs.json'; diff --git a/api_docs/kbn_visualization_utils.mdx b/api_docs/kbn_visualization_utils.mdx index d1a1ae70d904c..62fe4a021507c 100644 --- a/api_docs/kbn_visualization_utils.mdx +++ b/api_docs/kbn_visualization_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-visualization-utils title: "@kbn/visualization-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/visualization-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/visualization-utils'] --- import kbnVisualizationUtilsObj from './kbn_visualization_utils.devdocs.json'; diff --git a/api_docs/kbn_xstate_utils.mdx b/api_docs/kbn_xstate_utils.mdx index 51bebebd5627c..644c5e1a649f3 100644 --- a/api_docs/kbn_xstate_utils.mdx +++ b/api_docs/kbn_xstate_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-xstate-utils title: "@kbn/xstate-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/xstate-utils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/xstate-utils'] --- import kbnXstateUtilsObj from './kbn_xstate_utils.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index 1c75571d46dfa..a8b7180cf8ea0 100644 --- a/api_docs/kbn_yarn_lock_validator.mdx +++ b/api_docs/kbn_yarn_lock_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-yarn-lock-validator title: "@kbn/yarn-lock-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/yarn-lock-validator plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] --- import kbnYarnLockValidatorObj from './kbn_yarn_lock_validator.devdocs.json'; diff --git a/api_docs/kbn_zod.mdx b/api_docs/kbn_zod.mdx index b18b8dd13b7f3..0ed259a048ee0 100644 --- a/api_docs/kbn_zod.mdx +++ b/api_docs/kbn_zod.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-zod title: "@kbn/zod" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/zod plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/zod'] --- import kbnZodObj from './kbn_zod.devdocs.json'; diff --git a/api_docs/kbn_zod_helpers.mdx b/api_docs/kbn_zod_helpers.mdx index 8b7c66dd17a0b..6af4c06c27bef 100644 --- a/api_docs/kbn_zod_helpers.mdx +++ b/api_docs/kbn_zod_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-zod-helpers title: "@kbn/zod-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/zod-helpers plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/zod-helpers'] --- import kbnZodHelpersObj from './kbn_zod_helpers.devdocs.json'; diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index dc35cb9543739..89caa9dbe1d9f 100644 --- a/api_docs/kibana_overview.mdx +++ b/api_docs/kibana_overview.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaOverview title: "kibanaOverview" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaOverview plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaOverview'] --- import kibanaOverviewObj from './kibana_overview.devdocs.json'; diff --git a/api_docs/kibana_react.mdx b/api_docs/kibana_react.mdx index 79d5097b3a2a7..5e53dac5b9d0e 100644 --- a/api_docs/kibana_react.mdx +++ b/api_docs/kibana_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaReact title: "kibanaReact" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaReact plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaReact'] --- import kibanaReactObj from './kibana_react.devdocs.json'; diff --git a/api_docs/kibana_utils.mdx b/api_docs/kibana_utils.mdx index 4f9bb1f210abb..0d907a9afd1d5 100644 --- a/api_docs/kibana_utils.mdx +++ b/api_docs/kibana_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaUtils title: "kibanaUtils" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaUtils plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaUtils'] --- import kibanaUtilsObj from './kibana_utils.devdocs.json'; diff --git a/api_docs/kubernetes_security.mdx b/api_docs/kubernetes_security.mdx index 30f0ff53a4d35..24e8cd3fca7af 100644 --- a/api_docs/kubernetes_security.mdx +++ b/api_docs/kubernetes_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kubernetesSecurity title: "kubernetesSecurity" image: https://source.unsplash.com/400x175/?github description: API docs for the kubernetesSecurity plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] --- import kubernetesSecurityObj from './kubernetes_security.devdocs.json'; diff --git a/api_docs/lens.devdocs.json b/api_docs/lens.devdocs.json index fec200edeeb11..2c42b648debb8 100644 --- a/api_docs/lens.devdocs.json +++ b/api_docs/lens.devdocs.json @@ -11201,7 +11201,7 @@ "label": "FittingFunction", "description": [], "signature": [ - "\"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\"" + "\"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\"" ], "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts", "deprecated": false, diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index 4ddb3a041b87d..e20eb3653649b 100644 --- a/api_docs/lens.mdx +++ b/api_docs/lens.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lens title: "lens" image: https://source.unsplash.com/400x175/?github description: API docs for the lens plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens'] --- import lensObj from './lens.devdocs.json'; diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx index bb1d5d1ee03ab..9e7d375dfdfc1 100644 --- a/api_docs/license_api_guard.mdx +++ b/api_docs/license_api_guard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseApiGuard title: "licenseApiGuard" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseApiGuard plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseApiGuard'] --- import licenseApiGuardObj from './license_api_guard.devdocs.json'; diff --git a/api_docs/license_management.mdx b/api_docs/license_management.mdx index 9dfb7736feae5..a44f54ae80c6a 100644 --- a/api_docs/license_management.mdx +++ b/api_docs/license_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseManagement title: "licenseManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseManagement plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseManagement'] --- import licenseManagementObj from './license_management.devdocs.json'; diff --git a/api_docs/licensing.mdx b/api_docs/licensing.mdx index ee109ad476f36..93fc2d5722995 100644 --- a/api_docs/licensing.mdx +++ b/api_docs/licensing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licensing title: "licensing" image: https://source.unsplash.com/400x175/?github description: API docs for the licensing plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] --- import licensingObj from './licensing.devdocs.json'; diff --git a/api_docs/links.mdx b/api_docs/links.mdx index ee0b7c8313846..deb58685cf52a 100644 --- a/api_docs/links.mdx +++ b/api_docs/links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/links title: "links" image: https://source.unsplash.com/400x175/?github description: API docs for the links plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'links'] --- import linksObj from './links.devdocs.json'; diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index dc16dafe4330d..4d800922b99a9 100644 --- a/api_docs/lists.mdx +++ b/api_docs/lists.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lists title: "lists" image: https://source.unsplash.com/400x175/?github description: API docs for the lists plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/logs_data_access.mdx b/api_docs/logs_data_access.mdx index 8bc38ebbb6857..ae0f1aeaa80f3 100644 --- a/api_docs/logs_data_access.mdx +++ b/api_docs/logs_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsDataAccess title: "logsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the logsDataAccess plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsDataAccess'] --- import logsDataAccessObj from './logs_data_access.devdocs.json'; diff --git a/api_docs/logs_explorer.mdx b/api_docs/logs_explorer.mdx index 89db7d7c478dc..800ab4c6b6e99 100644 --- a/api_docs/logs_explorer.mdx +++ b/api_docs/logs_explorer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsExplorer title: "logsExplorer" image: https://source.unsplash.com/400x175/?github description: API docs for the logsExplorer plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsExplorer'] --- import logsExplorerObj from './logs_explorer.devdocs.json'; diff --git a/api_docs/logs_shared.mdx b/api_docs/logs_shared.mdx index add1dc04b6642..48875daaa5c3b 100644 --- a/api_docs/logs_shared.mdx +++ b/api_docs/logs_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsShared title: "logsShared" image: https://source.unsplash.com/400x175/?github description: API docs for the logsShared plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsShared'] --- import logsSharedObj from './logs_shared.devdocs.json'; diff --git a/api_docs/management.mdx b/api_docs/management.mdx index bf4cceb754d8a..db34cfcd1182a 100644 --- a/api_docs/management.mdx +++ b/api_docs/management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/management title: "management" image: https://source.unsplash.com/400x175/?github description: API docs for the management plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'management'] --- import managementObj from './management.devdocs.json'; diff --git a/api_docs/maps.mdx b/api_docs/maps.mdx index 578c75f938524..c97533264c26f 100644 --- a/api_docs/maps.mdx +++ b/api_docs/maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/maps title: "maps" image: https://source.unsplash.com/400x175/?github description: API docs for the maps plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'maps'] --- import mapsObj from './maps.devdocs.json'; diff --git a/api_docs/maps_ems.mdx b/api_docs/maps_ems.mdx index bc666eb8fda62..ddd07a7730286 100644 --- a/api_docs/maps_ems.mdx +++ b/api_docs/maps_ems.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mapsEms title: "mapsEms" image: https://source.unsplash.com/400x175/?github description: API docs for the mapsEms plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/metrics_data_access.devdocs.json b/api_docs/metrics_data_access.devdocs.json index ce7592c78d5f5..1709070d01746 100644 --- a/api_docs/metrics_data_access.devdocs.json +++ b/api_docs/metrics_data_access.devdocs.json @@ -1149,7 +1149,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly loadBreakdown: { title: string; dataset?: ", "LensDataset", @@ -1167,7 +1167,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly cpuUsage: { title: string; dataset?: ", "LensDataset", @@ -1185,7 +1185,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly normalizedLoad1m: { title: string; dataset?: ", "LensDataset", @@ -1203,7 +1203,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; }; readonly metric: { readonly cpuUsage: { title: string; dataset?: ", "LensDataset", @@ -1229,7 +1229,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly diskUsageByMountPoint: { title: string; dataset?: ", "LensDataset", @@ -1247,7 +1247,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly diskIOReadWrite: { title: string; dataset?: ", "LensDataset", @@ -1265,7 +1265,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly diskSpaceAvailable: { title: string; dataset?: ", "LensDataset", @@ -1283,7 +1283,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly diskIORead: { title: string; dataset?: ", "LensDataset", @@ -1301,7 +1301,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly diskIOWrite: { title: string; dataset?: ", "LensDataset", @@ -1319,7 +1319,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly diskReadThroughput: { title: string; dataset?: ", "LensDataset", @@ -1337,7 +1337,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly diskWriteThroughput: { title: string; dataset?: ", "LensDataset", @@ -1355,7 +1355,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; }; readonly metric: { readonly diskUsage: { title: string; dataset?: ", "LensDataset", @@ -1377,7 +1377,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; memoryUsage: { title: string; dataset?: ", "LensDataset", @@ -1395,7 +1395,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; memoryFree: { title: string; dataset?: ", "LensDataset", @@ -1413,7 +1413,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; }; metric: { memoryUsage: { title: string; dataset?: ", "LensDataset", @@ -1435,7 +1435,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly rx: { title: string; dataset?: ", "LensDataset", @@ -1453,7 +1453,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly tx: { title: string; dataset?: ", "LensDataset", @@ -1471,7 +1471,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; }; }; readonly logs: { xy: { logRate: { title: string; dataset?: ", "LensDataset", @@ -1489,7 +1489,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; }; }; readonly kibernetesNode: { readonly xy: { readonly nodeCpuCapacity: { title: string; dataset?: ", "LensDataset", @@ -1507,7 +1507,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly nodeMemoryCapacity: { title: string; dataset?: ", "LensDataset", @@ -1525,7 +1525,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly nodeDiskCapacity: { title: string; dataset?: ", "LensDataset", @@ -1543,7 +1543,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly nodePodCapacity: { title: string; dataset?: ", "LensDataset", @@ -1561,7 +1561,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; }; }; }>> | ", "InventoryModel", @@ -1603,7 +1603,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly k8sContainerCpuUsage: { title: string; dataset?: ", "LensDataset", @@ -1621,7 +1621,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; }; readonly metric: { readonly dockerContainerCpuUsage: { title: string; dataset?: ", "LensDataset", @@ -1647,7 +1647,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; k8sContainerMemoryUsage: { title: string; dataset?: ", "LensDataset", @@ -1665,7 +1665,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; }; metric: { dockerContainerMemoryUsage: { title: string; dataset?: ", "LensDataset", @@ -1691,7 +1691,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; }; }; readonly diskIO: { xy: { dockerContainerDiskIOReadWrite: { title: string; dataset?: ", "LensDataset", @@ -1709,7 +1709,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; }; }; }>>)[]" ], @@ -1804,7 +1804,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly loadBreakdown: { title: string; dataset?: ", "LensDataset", @@ -1822,7 +1822,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly cpuUsage: { title: string; dataset?: ", "LensDataset", @@ -1840,7 +1840,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly normalizedLoad1m: { title: string; dataset?: ", "LensDataset", @@ -1858,7 +1858,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; }; readonly metric: { readonly cpuUsage: { title: string; dataset?: ", "LensDataset", @@ -1884,7 +1884,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly diskUsageByMountPoint: { title: string; dataset?: ", "LensDataset", @@ -1902,7 +1902,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly diskIOReadWrite: { title: string; dataset?: ", "LensDataset", @@ -1920,7 +1920,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly diskSpaceAvailable: { title: string; dataset?: ", "LensDataset", @@ -1938,7 +1938,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly diskIORead: { title: string; dataset?: ", "LensDataset", @@ -1956,7 +1956,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly diskIOWrite: { title: string; dataset?: ", "LensDataset", @@ -1974,7 +1974,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly diskReadThroughput: { title: string; dataset?: ", "LensDataset", @@ -1992,7 +1992,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly diskWriteThroughput: { title: string; dataset?: ", "LensDataset", @@ -2010,7 +2010,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; }; readonly metric: { readonly diskUsage: { title: string; dataset?: ", "LensDataset", @@ -2032,7 +2032,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; memoryUsage: { title: string; dataset?: ", "LensDataset", @@ -2050,7 +2050,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; memoryFree: { title: string; dataset?: ", "LensDataset", @@ -2068,7 +2068,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; }; metric: { memoryUsage: { title: string; dataset?: ", "LensDataset", @@ -2090,7 +2090,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly rx: { title: string; dataset?: ", "LensDataset", @@ -2108,7 +2108,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly tx: { title: string; dataset?: ", "LensDataset", @@ -2126,7 +2126,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; }; }; readonly logs: { xy: { logRate: { title: string; dataset?: ", "LensDataset", @@ -2144,7 +2144,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; }; }; readonly kibernetesNode: { readonly xy: { readonly nodeCpuCapacity: { title: string; dataset?: ", "LensDataset", @@ -2162,7 +2162,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly nodeMemoryCapacity: { title: string; dataset?: ", "LensDataset", @@ -2180,7 +2180,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly nodeDiskCapacity: { title: string; dataset?: ", "LensDataset", @@ -2198,7 +2198,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly nodePodCapacity: { title: string; dataset?: ", "LensDataset", @@ -2216,7 +2216,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; }; }; }>>; readonly pod: ", "InventoryModel", @@ -2258,7 +2258,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; readonly k8sContainerCpuUsage: { title: string; dataset?: ", "LensDataset", @@ -2276,7 +2276,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; }; readonly metric: { readonly dockerContainerCpuUsage: { title: string; dataset?: ", "LensDataset", @@ -2302,7 +2302,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; k8sContainerMemoryUsage: { title: string; dataset?: ", "LensDataset", @@ -2320,7 +2320,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; }; metric: { dockerContainerMemoryUsage: { title: string; dataset?: ", "LensDataset", @@ -2346,7 +2346,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; }; }; readonly diskIO: { xy: { dockerContainerDiskIOReadWrite: { title: string; dataset?: ", "LensDataset", @@ -2364,7 +2364,7 @@ "LensBaseLayer", "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", "LensReferenceLineLayer", - ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"top\" | \"bottom\" | \"left\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined; yBounds?: ", "LensYBoundsConfig", " | undefined; } & { id: string; }; }; }; }>>; readonly awsEC2: ", "InventoryModel", diff --git a/api_docs/metrics_data_access.mdx b/api_docs/metrics_data_access.mdx index c8ce7832a3c50..ad85e5d55e5cc 100644 --- a/api_docs/metrics_data_access.mdx +++ b/api_docs/metrics_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/metricsDataAccess title: "metricsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the metricsDataAccess plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'metricsDataAccess'] --- import metricsDataAccessObj from './metrics_data_access.devdocs.json'; diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index f31946b0a401c..c1bdac945d38c 100644 --- a/api_docs/ml.mdx +++ b/api_docs/ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ml title: "ml" image: https://source.unsplash.com/400x175/?github description: API docs for the ml plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] --- import mlObj from './ml.devdocs.json'; diff --git a/api_docs/mock_idp_plugin.mdx b/api_docs/mock_idp_plugin.mdx index 2afefcaa4fc9e..5563cc25439eb 100644 --- a/api_docs/mock_idp_plugin.mdx +++ b/api_docs/mock_idp_plugin.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mockIdpPlugin title: "mockIdpPlugin" image: https://source.unsplash.com/400x175/?github description: API docs for the mockIdpPlugin plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mockIdpPlugin'] --- import mockIdpPluginObj from './mock_idp_plugin.devdocs.json'; diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index 28fa6b79600e5..9ee000563088b 100644 --- a/api_docs/monitoring.mdx +++ b/api_docs/monitoring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoring title: "monitoring" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoring plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoring'] --- import monitoringObj from './monitoring.devdocs.json'; diff --git a/api_docs/monitoring_collection.mdx b/api_docs/monitoring_collection.mdx index c2286aa7ae4fb..11b2df7046175 100644 --- a/api_docs/monitoring_collection.mdx +++ b/api_docs/monitoring_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoringCollection title: "monitoringCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoringCollection plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoringCollection'] --- import monitoringCollectionObj from './monitoring_collection.devdocs.json'; diff --git a/api_docs/navigation.devdocs.json b/api_docs/navigation.devdocs.json index 339e145f24c30..d1c3a65dac7cc 100644 --- a/api_docs/navigation.devdocs.json +++ b/api_docs/navigation.devdocs.json @@ -368,15 +368,7 @@ "label": "TopNavMenuItems", "description": [], "signature": [ - "({ config, className, }: { config: ", - { - "pluginId": "navigation", - "scope": "public", - "docId": "kibNavigationPluginApi", - "section": "def-public.TopNavMenuData", - "text": "TopNavMenuData" - }, - "[] | undefined; className?: string | undefined; }) => React.JSX.Element | null" + "({ config, className, popoverBreakpoints, }: TopNavMenuItemsProps) => React.JSX.Element | null" ], "path": "src/plugins/navigation/public/top_nav_menu/top_nav_menu_items.tsx", "deprecated": false, @@ -387,48 +379,15 @@ "id": "def-public.TopNavMenuItems.$1", "type": "Object", "tags": [], - "label": "{\n config,\n className,\n}", + "label": "{\n config,\n className,\n popoverBreakpoints,\n}", "description": [], + "signature": [ + "TopNavMenuItemsProps" + ], "path": "src/plugins/navigation/public/top_nav_menu/top_nav_menu_items.tsx", "deprecated": false, "trackAdoption": false, - "children": [ - { - "parentPluginId": "navigation", - "id": "def-public.TopNavMenuItems.$1.config", - "type": "Array", - "tags": [], - "label": "config", - "description": [], - "signature": [ - { - "pluginId": "navigation", - "scope": "public", - "docId": "kibNavigationPluginApi", - "section": "def-public.TopNavMenuData", - "text": "TopNavMenuData" - }, - "[] | undefined" - ], - "path": "src/plugins/navigation/public/top_nav_menu/top_nav_menu_items.tsx", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "navigation", - "id": "def-public.TopNavMenuItems.$1.className", - "type": "string", - "tags": [], - "label": "className", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/navigation/public/top_nav_menu/top_nav_menu_items.tsx", - "deprecated": false, - "trackAdoption": false - } - ] + "isRequired": true } ], "returnComment": [], @@ -976,7 +935,7 @@ "section": "def-public.MountPoint", "text": "MountPoint" }, - " | undefined) => void) | undefined; }" + " | undefined) => void) | undefined; popoverBreakpoints?: string[] | undefined; }" ], "path": "src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx", "deprecated": false, diff --git a/api_docs/navigation.mdx b/api_docs/navigation.mdx index 3a7124e948a7f..27b3ea3f0d23a 100644 --- a/api_docs/navigation.mdx +++ b/api_docs/navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/navigation title: "navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the navigation plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'navigation'] --- import navigationObj from './navigation.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sh | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 57 | 0 | 55 | 4 | +| 55 | 0 | 53 | 4 | ## Client diff --git a/api_docs/newsfeed.mdx b/api_docs/newsfeed.mdx index 3f4d365f6eeaa..c4e160739760c 100644 --- a/api_docs/newsfeed.mdx +++ b/api_docs/newsfeed.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/newsfeed title: "newsfeed" image: https://source.unsplash.com/400x175/?github description: API docs for the newsfeed plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/no_data_page.mdx b/api_docs/no_data_page.mdx index 6e9eed135f621..4909322b5f73c 100644 --- a/api_docs/no_data_page.mdx +++ b/api_docs/no_data_page.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/noDataPage title: "noDataPage" image: https://source.unsplash.com/400x175/?github description: API docs for the noDataPage plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'noDataPage'] --- import noDataPageObj from './no_data_page.devdocs.json'; diff --git a/api_docs/notifications.mdx b/api_docs/notifications.mdx index b77fdf0dec430..ae5405998423b 100644 --- a/api_docs/notifications.mdx +++ b/api_docs/notifications.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/notifications title: "notifications" image: https://source.unsplash.com/400x175/?github description: API docs for the notifications plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'notifications'] --- import notificationsObj from './notifications.devdocs.json'; diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 9cbb8c3d93fbb..83dcaff4d1d55 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observability title: "observability" image: https://source.unsplash.com/400x175/?github description: API docs for the observability plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; diff --git a/api_docs/observability_a_i_assistant.mdx b/api_docs/observability_a_i_assistant.mdx index 3bb574d077482..bcf7238135c15 100644 --- a/api_docs/observability_a_i_assistant.mdx +++ b/api_docs/observability_a_i_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAIAssistant title: "observabilityAIAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAIAssistant plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAIAssistant'] --- import observabilityAIAssistantObj from './observability_a_i_assistant.devdocs.json'; diff --git a/api_docs/observability_a_i_assistant_app.mdx b/api_docs/observability_a_i_assistant_app.mdx index 4bae412c939ee..48f6ec595ba47 100644 --- a/api_docs/observability_a_i_assistant_app.mdx +++ b/api_docs/observability_a_i_assistant_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAIAssistantApp title: "observabilityAIAssistantApp" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAIAssistantApp plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAIAssistantApp'] --- import observabilityAIAssistantAppObj from './observability_a_i_assistant_app.devdocs.json'; diff --git a/api_docs/observability_ai_assistant_management.mdx b/api_docs/observability_ai_assistant_management.mdx index 768f4a6b23d72..c39b37df500ac 100644 --- a/api_docs/observability_ai_assistant_management.mdx +++ b/api_docs/observability_ai_assistant_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAiAssistantManagement title: "observabilityAiAssistantManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAiAssistantManagement plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAiAssistantManagement'] --- import observabilityAiAssistantManagementObj from './observability_ai_assistant_management.devdocs.json'; diff --git a/api_docs/observability_logs_explorer.mdx b/api_docs/observability_logs_explorer.mdx index 487a1e6300d7c..f83c9d80f6698 100644 --- a/api_docs/observability_logs_explorer.mdx +++ b/api_docs/observability_logs_explorer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityLogsExplorer title: "observabilityLogsExplorer" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityLogsExplorer plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityLogsExplorer'] --- import observabilityLogsExplorerObj from './observability_logs_explorer.devdocs.json'; diff --git a/api_docs/observability_onboarding.mdx b/api_docs/observability_onboarding.mdx index 97ed1d8ff6e90..53bed513c8715 100644 --- a/api_docs/observability_onboarding.mdx +++ b/api_docs/observability_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityOnboarding title: "observabilityOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityOnboarding plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityOnboarding'] --- import observabilityOnboardingObj from './observability_onboarding.devdocs.json'; diff --git a/api_docs/observability_shared.mdx b/api_docs/observability_shared.mdx index ed008ed040f54..725a062db1007 100644 --- a/api_docs/observability_shared.mdx +++ b/api_docs/observability_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityShared title: "observabilityShared" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityShared plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityShared'] --- import observabilitySharedObj from './observability_shared.devdocs.json'; diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index 48bdf0c43482f..88456c91d166f 100644 --- a/api_docs/osquery.mdx +++ b/api_docs/osquery.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/osquery title: "osquery" image: https://source.unsplash.com/400x175/?github description: API docs for the osquery plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; diff --git a/api_docs/painless_lab.mdx b/api_docs/painless_lab.mdx index b5cadc87aa241..0cdafdd26c03c 100644 --- a/api_docs/painless_lab.mdx +++ b/api_docs/painless_lab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/painlessLab title: "painlessLab" image: https://source.unsplash.com/400x175/?github description: API docs for the painlessLab plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'painlessLab'] --- import painlessLabObj from './painless_lab.devdocs.json'; diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index 7dc1befef568f..a35c1a05f74ed 100644 --- a/api_docs/plugin_directory.mdx +++ b/api_docs/plugin_directory.mdx @@ -7,7 +7,7 @@ id: kibDevDocsPluginDirectory slug: /kibana-dev-docs/api-meta/plugin-api-directory title: Directory description: Directory of public APIs available through plugins or packages. -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -15,13 +15,13 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Count | Plugins or Packages with a
public API | Number of teams | |--------------|----------|------------------------| -| 874 | 746 | 45 | +| 875 | 747 | 45 | ### Public API health stats | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 53923 | 242 | 40510 | 2004 | +| 53994 | 242 | 40577 | 2008 | ## Plugin Directory @@ -95,7 +95,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds 'revealImage' function and renderer to expressions | 14 | 0 | 14 | 3 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds 'shape' function and renderer to expressions | 148 | 0 | 146 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Expression Tagcloud plugin adds a `tagcloud` renderer and function to the expression plugin. The renderer will display the `Wordcloud` chart. | 6 | 0 | 6 | 2 | -| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Expression XY plugin adds a `xy` renderer and function to the expression plugin. The renderer will display the `xy` chart. | 180 | 0 | 169 | 13 | +| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Expression XY plugin adds a `xy` renderer and function to the expression plugin. The renderer will display the `xy` chart. | 182 | 0 | 171 | 13 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Adds expression runtime to Kibana | 2235 | 17 | 1765 | 6 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 270 | 0 | 110 | 2 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Index pattern fields and ambiguous values formatters | 292 | 5 | 253 | 3 | @@ -103,7 +103,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | The file upload plugin contains components and services for uploading a file, analyzing its data, and then importing the data into an Elasticsearch index. Supported file types include CSV, TSV, newline-delimited JSON and GeoJSON. | 88 | 0 | 88 | 8 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | File upload, download, sharing, and serving over HTTP implementation in Kibana. | 240 | 0 | 24 | 9 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Simple UI for managing files in Kibana | 3 | 0 | 3 | 0 | -| | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1421 | 5 | 1296 | 76 | +| | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1426 | 5 | 1301 | 76 | | ftrApis | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 72 | 0 | 14 | 5 | | globalSearchBar | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 0 | 0 | 0 | 0 | @@ -148,7 +148,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 2 | 0 | 2 | 0 | | | [@elastic/stack-monitoring](https://github.com/orgs/elastic/teams/stack-monitoring) | - | 15 | 3 | 13 | 1 | | | [@elastic/stack-monitoring](https://github.com/orgs/elastic/teams/stack-monitoring) | - | 9 | 0 | 9 | 0 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 57 | 0 | 55 | 4 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 55 | 0 | 53 | 4 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 17 | 0 | 17 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 3 | 0 | 3 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 2 | 0 | 2 | 1 | @@ -260,7 +260,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 18 | 0 | 18 | 0 | | | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 4 | 0 | 4 | 0 | | | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 86 | 0 | 86 | 11 | -| | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 247 | 0 | 247 | 36 | +| | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 268 | 0 | 268 | 38 | | | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 337 | 0 | 336 | 0 | | | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 11 | 0 | 11 | 0 | | | [@elastic/security-defend-workflows](https://github.com/orgs/elastic/teams/security-defend-workflows) | - | 3 | 0 | 3 | 0 | @@ -331,7 +331,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 9 | 0 | 3 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 6 | 0 | 6 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 4 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 17 | 0 | 9 | 0 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 17 | 0 | 9 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 16 | 0 | 13 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 3 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 6 | 0 | 6 | 0 | @@ -372,7 +372,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 7 | 0 | 7 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 54 | 7 | 54 | 6 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 15 | 0 | 15 | 1 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 532 | 2 | 216 | 0 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 551 | 2 | 232 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 96 | 0 | 83 | 10 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 46 | 0 | 45 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 2 | 0 | @@ -463,7 +463,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 42 | 1 | 24 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 20 | 1 | 19 | 3 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 6 | 0 | 6 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 155 | 0 | 144 | 0 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 165 | 0 | 154 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 8 | 0 | 8 | 2 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 7 | 0 | 7 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 29 | 0 | 4 | 0 | @@ -518,7 +518,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 271 | 1 | 210 | 14 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 29 | 0 | 29 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 1 | 0 | -| | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 266 | 1 | 208 | 34 | +| | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 269 | 1 | 211 | 34 | | | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 29 | 0 | 12 | 0 | | | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 79 | 0 | 71 | 0 | | | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 202 | 0 | 190 | 12 | @@ -557,7 +557,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 87 | 0 | 79 | 6 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 41 | 2 | 35 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 9 | 0 | 7 | 0 | -| | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 108 | 0 | 107 | 0 | +| | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 116 | 0 | 115 | 0 | | | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 11 | 0 | 7 | 0 | | | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 193 | 0 | 190 | 6 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 172 | 0 | 172 | 1 | @@ -577,6 +577,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 88 | 0 | 10 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 56 | 0 | 6 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 2 | 0 | 0 | 0 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 1 | 0 | 0 | 0 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | - | 592 | 1 | 1 | 0 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | - | 2 | 0 | 2 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 95 | 1 | 0 | 0 | @@ -646,7 +647,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 40 | 0 | 38 | 5 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 13 | 0 | 9 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 6 | 0 | 6 | 1 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 96 | 0 | 86 | 13 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 99 | 0 | 89 | 13 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 13 | 0 | 13 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 52 | 0 | 52 | 3 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 25 | 0 | 21 | 0 | @@ -672,7 +673,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-kibana) | - | 76 | 0 | 76 | 0 | | | [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-kibana) | - | 3929 | 0 | 3929 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 18 | 1 | 17 | 1 | -| | [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-kibana) | - | 35 | 0 | 33 | 3 | +| | [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-kibana) | - | 36 | 0 | 34 | 3 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 20 | 0 | 18 | 1 | | | [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-kibana) | - | 2 | 0 | 2 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 50 | 0 | 25 | 0 | diff --git a/api_docs/presentation_panel.mdx b/api_docs/presentation_panel.mdx index a175781f0c786..d28928d9d7b46 100644 --- a/api_docs/presentation_panel.mdx +++ b/api_docs/presentation_panel.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationPanel title: "presentationPanel" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationPanel plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationPanel'] --- import presentationPanelObj from './presentation_panel.devdocs.json'; diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index 69bd4187944ed..0e95d62d360eb 100644 --- a/api_docs/presentation_util.mdx +++ b/api_docs/presentation_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationUtil title: "presentationUtil" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationUtil plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationUtil'] --- import presentationUtilObj from './presentation_util.devdocs.json'; diff --git a/api_docs/profiling.mdx b/api_docs/profiling.mdx index 39edea30f7a26..ae8e04b077b43 100644 --- a/api_docs/profiling.mdx +++ b/api_docs/profiling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profiling title: "profiling" image: https://source.unsplash.com/400x175/?github description: API docs for the profiling plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/profiling_data_access.mdx b/api_docs/profiling_data_access.mdx index 5469632aa0991..f4da3f0070988 100644 --- a/api_docs/profiling_data_access.mdx +++ b/api_docs/profiling_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profilingDataAccess title: "profilingDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the profilingDataAccess plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profilingDataAccess'] --- import profilingDataAccessObj from './profiling_data_access.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index 3ca0637fcbf2a..e82704d6719b3 100644 --- a/api_docs/remote_clusters.mdx +++ b/api_docs/remote_clusters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/remoteClusters title: "remoteClusters" image: https://source.unsplash.com/400x175/?github description: API docs for the remoteClusters plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'remoteClusters'] --- import remoteClustersObj from './remote_clusters.devdocs.json'; diff --git a/api_docs/reporting.mdx b/api_docs/reporting.mdx index e12787a5b19c1..f64c5dd406531 100644 --- a/api_docs/reporting.mdx +++ b/api_docs/reporting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/reporting title: "reporting" image: https://source.unsplash.com/400x175/?github description: API docs for the reporting plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'reporting'] --- import reportingObj from './reporting.devdocs.json'; diff --git a/api_docs/rollup.mdx b/api_docs/rollup.mdx index 813860e055413..e953d84a014e8 100644 --- a/api_docs/rollup.mdx +++ b/api_docs/rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/rollup title: "rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the rollup plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'rollup'] --- import rollupObj from './rollup.devdocs.json'; diff --git a/api_docs/rule_registry.mdx b/api_docs/rule_registry.mdx index 1213e3d751169..987f5ab06c2ac 100644 --- a/api_docs/rule_registry.mdx +++ b/api_docs/rule_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ruleRegistry title: "ruleRegistry" image: https://source.unsplash.com/400x175/?github description: API docs for the ruleRegistry plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ruleRegistry'] --- import ruleRegistryObj from './rule_registry.devdocs.json'; diff --git a/api_docs/runtime_fields.mdx b/api_docs/runtime_fields.mdx index 3586eb38cd421..b155f8dcc25d5 100644 --- a/api_docs/runtime_fields.mdx +++ b/api_docs/runtime_fields.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/runtimeFields title: "runtimeFields" image: https://source.unsplash.com/400x175/?github description: API docs for the runtimeFields plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'runtimeFields'] --- import runtimeFieldsObj from './runtime_fields.devdocs.json'; diff --git a/api_docs/saved_objects.mdx b/api_docs/saved_objects.mdx index 806d7c971c9e0..993dd40ebc3d0 100644 --- a/api_docs/saved_objects.mdx +++ b/api_docs/saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjects title: "savedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjects plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjects'] --- import savedObjectsObj from './saved_objects.devdocs.json'; diff --git a/api_docs/saved_objects_finder.mdx b/api_docs/saved_objects_finder.mdx index 8fbb5ef61cb4a..38ec230e70303 100644 --- a/api_docs/saved_objects_finder.mdx +++ b/api_docs/saved_objects_finder.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsFinder title: "savedObjectsFinder" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsFinder plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsFinder'] --- import savedObjectsFinderObj from './saved_objects_finder.devdocs.json'; diff --git a/api_docs/saved_objects_management.mdx b/api_docs/saved_objects_management.mdx index fd4dabed55698..edbd31275d247 100644 --- a/api_docs/saved_objects_management.mdx +++ b/api_docs/saved_objects_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsManagement title: "savedObjectsManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsManagement plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsManagement'] --- import savedObjectsManagementObj from './saved_objects_management.devdocs.json'; diff --git a/api_docs/saved_objects_tagging.mdx b/api_docs/saved_objects_tagging.mdx index 44fca63ea762c..061cd64ee667f 100644 --- a/api_docs/saved_objects_tagging.mdx +++ b/api_docs/saved_objects_tagging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTagging title: "savedObjectsTagging" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTagging plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTagging'] --- import savedObjectsTaggingObj from './saved_objects_tagging.devdocs.json'; diff --git a/api_docs/saved_objects_tagging_oss.mdx b/api_docs/saved_objects_tagging_oss.mdx index a83c41b5e603f..fed7164e22ecb 100644 --- a/api_docs/saved_objects_tagging_oss.mdx +++ b/api_docs/saved_objects_tagging_oss.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTaggingOss title: "savedObjectsTaggingOss" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTaggingOss plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTaggingOss'] --- import savedObjectsTaggingOssObj from './saved_objects_tagging_oss.devdocs.json'; diff --git a/api_docs/saved_search.mdx b/api_docs/saved_search.mdx index f5e3c09b7a7eb..31aec7280c720 100644 --- a/api_docs/saved_search.mdx +++ b/api_docs/saved_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedSearch title: "savedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the savedSearch plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedSearch'] --- import savedSearchObj from './saved_search.devdocs.json'; diff --git a/api_docs/screenshot_mode.mdx b/api_docs/screenshot_mode.mdx index fbbf9b20f24aa..11cb91a52679a 100644 --- a/api_docs/screenshot_mode.mdx +++ b/api_docs/screenshot_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotMode title: "screenshotMode" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotMode plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotMode'] --- import screenshotModeObj from './screenshot_mode.devdocs.json'; diff --git a/api_docs/screenshotting.mdx b/api_docs/screenshotting.mdx index f0feb822c23b1..3ae33f5280a9a 100644 --- a/api_docs/screenshotting.mdx +++ b/api_docs/screenshotting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotting title: "screenshotting" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotting plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/search_assistant.mdx b/api_docs/search_assistant.mdx index 97b6ec3f4dddc..bb8277ffcc6fa 100644 --- a/api_docs/search_assistant.mdx +++ b/api_docs/search_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchAssistant title: "searchAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the searchAssistant plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchAssistant'] --- import searchAssistantObj from './search_assistant.devdocs.json'; diff --git a/api_docs/search_connectors.mdx b/api_docs/search_connectors.mdx index 7f81dd72dfc22..57bce6557ef5c 100644 --- a/api_docs/search_connectors.mdx +++ b/api_docs/search_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchConnectors title: "searchConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the searchConnectors plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchConnectors'] --- import searchConnectorsObj from './search_connectors.devdocs.json'; diff --git a/api_docs/search_homepage.mdx b/api_docs/search_homepage.mdx index 647d084252580..ca792560dac70 100644 --- a/api_docs/search_homepage.mdx +++ b/api_docs/search_homepage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchHomepage title: "searchHomepage" image: https://source.unsplash.com/400x175/?github description: API docs for the searchHomepage plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchHomepage'] --- import searchHomepageObj from './search_homepage.devdocs.json'; diff --git a/api_docs/search_indices.mdx b/api_docs/search_indices.mdx index 48fcf865b8ad6..71149f0526577 100644 --- a/api_docs/search_indices.mdx +++ b/api_docs/search_indices.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchIndices title: "searchIndices" image: https://source.unsplash.com/400x175/?github description: API docs for the searchIndices plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchIndices'] --- import searchIndicesObj from './search_indices.devdocs.json'; diff --git a/api_docs/search_inference_endpoints.mdx b/api_docs/search_inference_endpoints.mdx index dcaeadb92f8bd..cda9aea2d5d0d 100644 --- a/api_docs/search_inference_endpoints.mdx +++ b/api_docs/search_inference_endpoints.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchInferenceEndpoints title: "searchInferenceEndpoints" image: https://source.unsplash.com/400x175/?github description: API docs for the searchInferenceEndpoints plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchInferenceEndpoints'] --- import searchInferenceEndpointsObj from './search_inference_endpoints.devdocs.json'; diff --git a/api_docs/search_notebooks.mdx b/api_docs/search_notebooks.mdx index 7b6e54cf39420..c9ec072e31cea 100644 --- a/api_docs/search_notebooks.mdx +++ b/api_docs/search_notebooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchNotebooks title: "searchNotebooks" image: https://source.unsplash.com/400x175/?github description: API docs for the searchNotebooks plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchNotebooks'] --- import searchNotebooksObj from './search_notebooks.devdocs.json'; diff --git a/api_docs/search_playground.mdx b/api_docs/search_playground.mdx index 331a7d9c8a09e..82824168be9be 100644 --- a/api_docs/search_playground.mdx +++ b/api_docs/search_playground.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchPlayground title: "searchPlayground" image: https://source.unsplash.com/400x175/?github description: API docs for the searchPlayground plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchPlayground'] --- import searchPlaygroundObj from './search_playground.devdocs.json'; diff --git a/api_docs/security.mdx b/api_docs/security.mdx index 052061dacd1b6..acafa149b5dfd 100644 --- a/api_docs/security.mdx +++ b/api_docs/security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/security title: "security" image: https://source.unsplash.com/400x175/?github description: API docs for the security plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'security'] --- import securityObj from './security.devdocs.json'; diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index e1e4a2a328d35..7e9f8d67b7df7 100644 --- a/api_docs/security_solution.mdx +++ b/api_docs/security_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolution title: "securitySolution" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolution plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; diff --git a/api_docs/security_solution_ess.mdx b/api_docs/security_solution_ess.mdx index fd8f1b46b279f..071bc473f2d76 100644 --- a/api_docs/security_solution_ess.mdx +++ b/api_docs/security_solution_ess.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionEss title: "securitySolutionEss" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionEss plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionEss'] --- import securitySolutionEssObj from './security_solution_ess.devdocs.json'; diff --git a/api_docs/security_solution_serverless.mdx b/api_docs/security_solution_serverless.mdx index f1987f89b933e..ac7994c0f70dd 100644 --- a/api_docs/security_solution_serverless.mdx +++ b/api_docs/security_solution_serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionServerless title: "securitySolutionServerless" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionServerless plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionServerless'] --- import securitySolutionServerlessObj from './security_solution_serverless.devdocs.json'; diff --git a/api_docs/serverless.mdx b/api_docs/serverless.mdx index da3f08ae55ec2..160077d47ed37 100644 --- a/api_docs/serverless.mdx +++ b/api_docs/serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverless title: "serverless" image: https://source.unsplash.com/400x175/?github description: API docs for the serverless plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverless'] --- import serverlessObj from './serverless.devdocs.json'; diff --git a/api_docs/serverless_observability.mdx b/api_docs/serverless_observability.mdx index 1ff33264adc20..64350dd1762f2 100644 --- a/api_docs/serverless_observability.mdx +++ b/api_docs/serverless_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessObservability title: "serverlessObservability" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessObservability plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessObservability'] --- import serverlessObservabilityObj from './serverless_observability.devdocs.json'; diff --git a/api_docs/serverless_search.mdx b/api_docs/serverless_search.mdx index a346767cacb8e..f8e9805d1dca0 100644 --- a/api_docs/serverless_search.mdx +++ b/api_docs/serverless_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessSearch title: "serverlessSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessSearch plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessSearch'] --- import serverlessSearchObj from './serverless_search.devdocs.json'; diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index d05b6883b9436..d158f70ffc914 100644 --- a/api_docs/session_view.mdx +++ b/api_docs/session_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/sessionView title: "sessionView" image: https://source.unsplash.com/400x175/?github description: API docs for the sessionView plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'sessionView'] --- import sessionViewObj from './session_view.devdocs.json'; diff --git a/api_docs/share.mdx b/api_docs/share.mdx index 7492c9b17d57c..301f22da7099c 100644 --- a/api_docs/share.mdx +++ b/api_docs/share.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/share title: "share" image: https://source.unsplash.com/400x175/?github description: API docs for the share plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/slo.mdx b/api_docs/slo.mdx index 107d993a42dd0..288e796fc9f2c 100644 --- a/api_docs/slo.mdx +++ b/api_docs/slo.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/slo title: "slo" image: https://source.unsplash.com/400x175/?github description: API docs for the slo plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'slo'] --- import sloObj from './slo.devdocs.json'; diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index 532beb9389ea7..c227cd6b521ea 100644 --- a/api_docs/snapshot_restore.mdx +++ b/api_docs/snapshot_restore.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/snapshotRestore title: "snapshotRestore" image: https://source.unsplash.com/400x175/?github description: API docs for the snapshotRestore plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'snapshotRestore'] --- import snapshotRestoreObj from './snapshot_restore.devdocs.json'; diff --git a/api_docs/spaces.mdx b/api_docs/spaces.mdx index 33e8ef651d041..5cbab96cd73d1 100644 --- a/api_docs/spaces.mdx +++ b/api_docs/spaces.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/spaces title: "spaces" image: https://source.unsplash.com/400x175/?github description: API docs for the spaces plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'spaces'] --- import spacesObj from './spaces.devdocs.json'; diff --git a/api_docs/stack_alerts.mdx b/api_docs/stack_alerts.mdx index 28a43a49f6522..706deaed61aa6 100644 --- a/api_docs/stack_alerts.mdx +++ b/api_docs/stack_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackAlerts title: "stackAlerts" image: https://source.unsplash.com/400x175/?github description: API docs for the stackAlerts plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackAlerts'] --- import stackAlertsObj from './stack_alerts.devdocs.json'; diff --git a/api_docs/stack_connectors.mdx b/api_docs/stack_connectors.mdx index c1d47c34ccc36..001b54e5324b4 100644 --- a/api_docs/stack_connectors.mdx +++ b/api_docs/stack_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackConnectors title: "stackConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the stackConnectors plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackConnectors'] --- import stackConnectorsObj from './stack_connectors.devdocs.json'; diff --git a/api_docs/task_manager.mdx b/api_docs/task_manager.mdx index 7aeb37aaecde5..9e92f0545ddcd 100644 --- a/api_docs/task_manager.mdx +++ b/api_docs/task_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/taskManager title: "taskManager" image: https://source.unsplash.com/400x175/?github description: API docs for the taskManager plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'taskManager'] --- import taskManagerObj from './task_manager.devdocs.json'; diff --git a/api_docs/telemetry.mdx b/api_docs/telemetry.mdx index 10a0baa04d17e..4b10e1b6625e8 100644 --- a/api_docs/telemetry.mdx +++ b/api_docs/telemetry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetry title: "telemetry" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetry plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetry'] --- import telemetryObj from './telemetry.devdocs.json'; diff --git a/api_docs/telemetry_collection_manager.mdx b/api_docs/telemetry_collection_manager.mdx index 9aeb923ada087..1a97ff3ddca80 100644 --- a/api_docs/telemetry_collection_manager.mdx +++ b/api_docs/telemetry_collection_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionManager title: "telemetryCollectionManager" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionManager plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionManager'] --- import telemetryCollectionManagerObj from './telemetry_collection_manager.devdocs.json'; diff --git a/api_docs/telemetry_collection_xpack.mdx b/api_docs/telemetry_collection_xpack.mdx index f2ce58678d7ea..7a68033c55b9f 100644 --- a/api_docs/telemetry_collection_xpack.mdx +++ b/api_docs/telemetry_collection_xpack.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionXpack title: "telemetryCollectionXpack" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionXpack plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionXpack'] --- import telemetryCollectionXpackObj from './telemetry_collection_xpack.devdocs.json'; diff --git a/api_docs/telemetry_management_section.mdx b/api_docs/telemetry_management_section.mdx index 6bed696faf539..57dd1d582df8e 100644 --- a/api_docs/telemetry_management_section.mdx +++ b/api_docs/telemetry_management_section.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryManagementSection title: "telemetryManagementSection" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryManagementSection plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] --- import telemetryManagementSectionObj from './telemetry_management_section.devdocs.json'; diff --git a/api_docs/threat_intelligence.mdx b/api_docs/threat_intelligence.mdx index ce5a8486771f9..1305052edfb9a 100644 --- a/api_docs/threat_intelligence.mdx +++ b/api_docs/threat_intelligence.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/threatIntelligence title: "threatIntelligence" image: https://source.unsplash.com/400x175/?github description: API docs for the threatIntelligence plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'threatIntelligence'] --- import threatIntelligenceObj from './threat_intelligence.devdocs.json'; diff --git a/api_docs/timelines.mdx b/api_docs/timelines.mdx index 0e83b58963fba..366d08dc22185 100644 --- a/api_docs/timelines.mdx +++ b/api_docs/timelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/timelines title: "timelines" image: https://source.unsplash.com/400x175/?github description: API docs for the timelines plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'timelines'] --- import timelinesObj from './timelines.devdocs.json'; diff --git a/api_docs/transform.mdx b/api_docs/transform.mdx index 4ce6df40f685d..5c58ce988ce89 100644 --- a/api_docs/transform.mdx +++ b/api_docs/transform.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/transform title: "transform" image: https://source.unsplash.com/400x175/?github description: API docs for the transform plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] --- import transformObj from './transform.devdocs.json'; diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index 3d6b339960050..511df7c0bcb3a 100644 --- a/api_docs/triggers_actions_ui.mdx +++ b/api_docs/triggers_actions_ui.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/triggersActionsUi title: "triggersActionsUi" image: https://source.unsplash.com/400x175/?github description: API docs for the triggersActionsUi plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] --- import triggersActionsUiObj from './triggers_actions_ui.devdocs.json'; diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index e1d26a68313b7..b3209b954f339 100644 --- a/api_docs/ui_actions.mdx +++ b/api_docs/ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActions title: "uiActions" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActions plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActions'] --- import uiActionsObj from './ui_actions.devdocs.json'; diff --git a/api_docs/ui_actions_enhanced.mdx b/api_docs/ui_actions_enhanced.mdx index 634fe17375fca..2533be85ee20c 100644 --- a/api_docs/ui_actions_enhanced.mdx +++ b/api_docs/ui_actions_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActionsEnhanced title: "uiActionsEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActionsEnhanced plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_doc_viewer.mdx b/api_docs/unified_doc_viewer.mdx index e0633220b4077..8ca3470dd6f6f 100644 --- a/api_docs/unified_doc_viewer.mdx +++ b/api_docs/unified_doc_viewer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedDocViewer title: "unifiedDocViewer" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedDocViewer plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedDocViewer'] --- import unifiedDocViewerObj from './unified_doc_viewer.devdocs.json'; diff --git a/api_docs/unified_histogram.mdx b/api_docs/unified_histogram.mdx index a0ea871fb6d6d..e0012036d3739 100644 --- a/api_docs/unified_histogram.mdx +++ b/api_docs/unified_histogram.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedHistogram title: "unifiedHistogram" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedHistogram plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedHistogram'] --- import unifiedHistogramObj from './unified_histogram.devdocs.json'; diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index 78ed8904a6cc9..c0348936979b0 100644 --- a/api_docs/unified_search.mdx +++ b/api_docs/unified_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch title: "unifiedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch'] --- import unifiedSearchObj from './unified_search.devdocs.json'; diff --git a/api_docs/unified_search_autocomplete.mdx b/api_docs/unified_search_autocomplete.mdx index abf573cd0bdc7..16131c2c79919 100644 --- a/api_docs/unified_search_autocomplete.mdx +++ b/api_docs/unified_search_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch-autocomplete title: "unifiedSearch.autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch.autocomplete plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; diff --git a/api_docs/uptime.mdx b/api_docs/uptime.mdx index 29220dc4ec390..8637b8e308f81 100644 --- a/api_docs/uptime.mdx +++ b/api_docs/uptime.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uptime title: "uptime" image: https://source.unsplash.com/400x175/?github description: API docs for the uptime plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uptime'] --- import uptimeObj from './uptime.devdocs.json'; diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index 8c99ac9288d83..7c3ce38e22076 100644 --- a/api_docs/url_forwarding.mdx +++ b/api_docs/url_forwarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/urlForwarding title: "urlForwarding" image: https://source.unsplash.com/400x175/?github description: API docs for the urlForwarding plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'urlForwarding'] --- import urlForwardingObj from './url_forwarding.devdocs.json'; diff --git a/api_docs/usage_collection.mdx b/api_docs/usage_collection.mdx index eb3ea00720176..e062817d6231e 100644 --- a/api_docs/usage_collection.mdx +++ b/api_docs/usage_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/usageCollection title: "usageCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the usageCollection plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'usageCollection'] --- import usageCollectionObj from './usage_collection.devdocs.json'; diff --git a/api_docs/ux.mdx b/api_docs/ux.mdx index e1443d3e30c60..7848a4f61bdb6 100644 --- a/api_docs/ux.mdx +++ b/api_docs/ux.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ux title: "ux" image: https://source.unsplash.com/400x175/?github description: API docs for the ux plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ux'] --- import uxObj from './ux.devdocs.json'; diff --git a/api_docs/vis_default_editor.mdx b/api_docs/vis_default_editor.mdx index ea238e256514e..2eae64c50a84e 100644 --- a/api_docs/vis_default_editor.mdx +++ b/api_docs/vis_default_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visDefaultEditor title: "visDefaultEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the visDefaultEditor plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visDefaultEditor'] --- import visDefaultEditorObj from './vis_default_editor.devdocs.json'; diff --git a/api_docs/vis_type_gauge.mdx b/api_docs/vis_type_gauge.mdx index 62b3978595945..c49b3cdca4e33 100644 --- a/api_docs/vis_type_gauge.mdx +++ b/api_docs/vis_type_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeGauge title: "visTypeGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeGauge plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeGauge'] --- import visTypeGaugeObj from './vis_type_gauge.devdocs.json'; diff --git a/api_docs/vis_type_heatmap.mdx b/api_docs/vis_type_heatmap.mdx index 90ff7e31ce406..a635e54904e8a 100644 --- a/api_docs/vis_type_heatmap.mdx +++ b/api_docs/vis_type_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeHeatmap title: "visTypeHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeHeatmap plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeHeatmap'] --- import visTypeHeatmapObj from './vis_type_heatmap.devdocs.json'; diff --git a/api_docs/vis_type_pie.mdx b/api_docs/vis_type_pie.mdx index 69313034879ed..6726f6a9d1767 100644 --- a/api_docs/vis_type_pie.mdx +++ b/api_docs/vis_type_pie.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypePie title: "visTypePie" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypePie plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypePie'] --- import visTypePieObj from './vis_type_pie.devdocs.json'; diff --git a/api_docs/vis_type_table.mdx b/api_docs/vis_type_table.mdx index 72e02e402d01d..45e8537ced6f5 100644 --- a/api_docs/vis_type_table.mdx +++ b/api_docs/vis_type_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTable title: "visTypeTable" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTable plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTable'] --- import visTypeTableObj from './vis_type_table.devdocs.json'; diff --git a/api_docs/vis_type_timelion.mdx b/api_docs/vis_type_timelion.mdx index 7d62bcfd8ea20..6f34b1f2e6ac2 100644 --- a/api_docs/vis_type_timelion.mdx +++ b/api_docs/vis_type_timelion.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimelion title: "visTypeTimelion" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimelion plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimelion'] --- import visTypeTimelionObj from './vis_type_timelion.devdocs.json'; diff --git a/api_docs/vis_type_timeseries.mdx b/api_docs/vis_type_timeseries.mdx index 9b8a3ff9d7f73..8440e636aeb65 100644 --- a/api_docs/vis_type_timeseries.mdx +++ b/api_docs/vis_type_timeseries.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimeseries title: "visTypeTimeseries" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimeseries plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimeseries'] --- import visTypeTimeseriesObj from './vis_type_timeseries.devdocs.json'; diff --git a/api_docs/vis_type_vega.mdx b/api_docs/vis_type_vega.mdx index 0b143809176b0..525347351153a 100644 --- a/api_docs/vis_type_vega.mdx +++ b/api_docs/vis_type_vega.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVega title: "visTypeVega" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVega plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVega'] --- import visTypeVegaObj from './vis_type_vega.devdocs.json'; diff --git a/api_docs/vis_type_vislib.mdx b/api_docs/vis_type_vislib.mdx index 8d7d1ad2f8b71..32c2921db5a10 100644 --- a/api_docs/vis_type_vislib.mdx +++ b/api_docs/vis_type_vislib.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVislib title: "visTypeVislib" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVislib plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVislib'] --- import visTypeVislibObj from './vis_type_vislib.devdocs.json'; diff --git a/api_docs/vis_type_xy.mdx b/api_docs/vis_type_xy.mdx index d44c2715112cb..5cf5b05ffcfe5 100644 --- a/api_docs/vis_type_xy.mdx +++ b/api_docs/vis_type_xy.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeXy title: "visTypeXy" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeXy plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] --- import visTypeXyObj from './vis_type_xy.devdocs.json'; diff --git a/api_docs/visualizations.devdocs.json b/api_docs/visualizations.devdocs.json index 281b4a8d767c1..4ffa3d6ca8856 100644 --- a/api_docs/visualizations.devdocs.json +++ b/api_docs/visualizations.devdocs.json @@ -13862,7 +13862,7 @@ "label": "fittingFunction", "description": [], "signature": [ - "\"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined" + "\"None\" | \"Average\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | \"Nearest\" | undefined" ], "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", "deprecated": false, diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index d96790ca9b419..2124c854ffc84 100644 --- a/api_docs/visualizations.mdx +++ b/api_docs/visualizations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visualizations title: "visualizations" image: https://source.unsplash.com/400x175/?github description: API docs for the visualizations plugin -date: 2024-10-21 +date: 2024-10-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; diff --git a/config/serverless.es.yml b/config/serverless.es.yml index eb3af92c89963..b3b953e5316ac 100644 --- a/config/serverless.es.yml +++ b/config/serverless.es.yml @@ -120,4 +120,4 @@ xpack.searchInferenceEndpoints.ui.enabled: false xpack.search.notebooks.catalog.url: https://elastic-enterprise-search.s3.us-east-2.amazonaws.com/serverless/catalog.json # Semantic text UI -xpack.index_management.dev.enableSemanticText: false +xpack.index_management.dev.enableSemanticText: true diff --git a/config/serverless.oblt.yml b/config/serverless.oblt.yml index 1146a9280ac4e..2155565a63ef2 100644 --- a/config/serverless.oblt.yml +++ b/config/serverless.oblt.yml @@ -207,3 +207,6 @@ xpack.ml.compatibleModuleType: 'observability' # Disable the embedded Dev Console console.ui.embeddedEnabled: false + +# Disable role management (custom roles) +xpack.security.roleManagementEnabled: false diff --git a/config/serverless.yml b/config/serverless.yml index 4249d8ff786ec..ec857577f1863 100644 --- a/config/serverless.yml +++ b/config/serverless.yml @@ -155,7 +155,7 @@ server.versioned.versionResolution: newest server.versioned.strictClientVersionCheck: false # Enforce single "default" space and disable feature visibility controls -xpack.spaces.maxSpaces: 1 +xpack.spaces.maxSpaces: 100 xpack.spaces.allowFeatureVisibility: false xpack.spaces.allowSolutionVisibility: false diff --git a/dev_docs/getting_started/hello_world_plugin.mdx b/dev_docs/getting_started/hello_world_plugin.mdx index 08fd7e4a721c2..29e6944f88150 100644 --- a/dev_docs/getting_started/hello_world_plugin.mdx +++ b/dev_docs/getting_started/hello_world_plugin.mdx @@ -90,7 +90,7 @@ And add the following to it: ```ts import React from 'react'; import ReactDOM from 'react-dom'; -import { AppMountParameters, CoreSetup, CoreStart, Plugin } from '../../../src/core/public'; +import { AppMountParameters, CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; export class HelloWorldPlugin implements Plugin { public setup(core: CoreSetup) { diff --git a/dev_docs/key_concepts/api_authorization.mdx b/dev_docs/key_concepts/api_authorization.mdx new file mode 100644 index 0000000000000..b781808757c9a --- /dev/null +++ b/dev_docs/key_concepts/api_authorization.mdx @@ -0,0 +1,319 @@ +--- +id: kibDevDocsSecurityAPIAuthorization +slug: /kibana-dev-docs/key-concepts/security-api-authorization +title: Kibana API authorization +description: This guide provides an overview of API authorization in Kibana. +date: 2024-10-04 +tags: ['kibana', 'dev', 'contributor', 'security'] +--- + +Authorization is an important aspect of API design. It must be considered for all endpoints, even those marked as `internal`. This guide explains how and when to apply authorization to your endpoints + +Table of contents: +1. [API authorization](#api-authorization) +2. [[Deprecated] Adding API authorization with `access` tags](#deprecated-adding-api-authorization-with-access-tags) + - [Why not add `access` tags to all routes by default?](#why-not-add-access-tags-to-all-routes-by-default) +3. [Adding API authorization with `security` configuration](#adding-api-authorization-with-security-configuration) + - [Key features](#key-features) + - [Configuring authorization on routes](#configuring-authorization-on-routes) + - [Opting out of authorization for specific routes](#opting-out-of-authorization-for-specific-routes) + - [Classic router security configuration examples](#classic-router-security-configuration-examples) + - [Versioned router security configuration examples](#versioned-router-security-configuration-examples) +4. [Authorization response available in route handlers](#authorization-response-available-in-route-handlers) +5. [OpenAPI specification (OAS) documentation](#openapi-specification-oas-documentation) +6. [Migrating from `access` tags to `security` configuration](#migrating-from-access-tags-to-security-configuration) +7. [Questions?](#questions) + +## API authorization +Kibana API routes do not have any authorization checks applied by default. This means that your APIs are accessible to anyone with valid credentials, regardless of their permissions. This includes users with no roles assigned. +This on its own is insufficient, and care must be taken to ensure that only authorized users can invoke your endpoints. + +Kibana leverages for a majority of its persistence. The Saved Objects Service performs its own authorization checks, so if your API route is primarily a CRUD interface to Saved Objects, then your authorization needs are likely already met. +This is also true for derivatives of the Saved Objects Service, such as the Alerting and Cases services. + +If your endpoint is not a CRUD interface to Saved Objects, or if your endpoint bypasses our built-in Saved Objects authorization checks, then you must ensure that only authorized users can invoke your endpoint. +This is **especially** important if your route does any of the following: +1. Performs non-insignificant processing, causing load on the Elasticsearch cluster or the Kibana server. +2. Calls Elasticsearch APIs using the internal `kibana_system` user. +3. Calls a third-party service. +4. Exposes any non-public information to the caller, such as system configuration or state, as part of the successful or even error response. + +## [Deprecated] Adding API authorization with `access` tags +**Note**: `access` tags were deprecated in favour of `security` configuration. + +`access` tags are used to restrict access to API routes. They are used to ensure that only users with the required privileges can access the route. + +Example configuration: +```ts +router.get({ + path: '/api/path', + options: { + tags: ['access:', 'access:'], + }, + ... +}, handler); +``` + +More information on adding `access` tags to your routes can be found temporarily in the [legacy documentation](https://www.elastic.co/guide/en/kibana/current/development-security.html#development-plugin-feature-registration) + +### Why not add `access` tags to all routes by default? +Each authorization check that we perform involves a round-trip to Elasticsearch, so they are not as cheap as we'd like. Therefore, we want to keep the number of authorization checks we perform within a single route to a minimum. +Adding an `access` tag to routes that leverage the Saved Objects Service would be redundant in most cases, since the Saved Objects Service will be performing authorization anyway. + + +## Adding API authorization with `security` configuration +`KibanaRouteOptions` provides a security configuration at the route definition level, offering robust security configurations for both **Classic** and **Versioned** routes. + +### Key features: +1. **Fine-grained control**: + - Define the exact privileges required to access the route. + - Use `requiredPrivileges` to specify privileges with support for complex rules: + - **AND rules** using `allRequired`: Requires all specified privileges for access. + - **OR rules** using `anyRequired`: Allows access if any one of the specified privileges is met. + - **Complex Nested Rules**: Combine both `allRequired` and `anyRequired` for advanced access rules. +2. **Explicit Opt-out**: Provide a reason for opting out of authorization to maintain transparency. +3. **Versioned Routes**: Define security configurations for different versions of the same route. +4. **Improved Documentation with OpenAPI (OAS)**: Automatically generated OAS documentation with the required privileges for each route. +5. **AuthzResult Object in Route Handlers**: Access the authorization response in route handlers to see which privileges were met. + + +### Configuring authorization on routes +**Before migration:** +```ts +router.get({ + path: '/api/path', + options: { + tags: ['access:', 'access:'], + }, + ... +}, handler); +``` + +**After migration:** +```ts +router.get({ + path: '/api/path', + security: { + authz: { + requiredPrivileges: ['', ''], + }, + }, + ... +}, handler); +``` + +### Opting out of authorization for specific routes +**Before migration:** +```ts +router.get({ + path: '/api/path', + ... +}, handler); +``` + +**After migration:** +```ts +router.get({ + path: '/api/path', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization because ...', + }, + }, + ... +}, handler); +``` + +### Classic router security configuration examples + +**Example 1: All privileges required.** +Requires `` AND `` to access the route. +```ts +router.get({ + path: '/api/path', + security: { + authz: { + requiredPrivileges: ['', ''], + }, + }, + ... +}, handler); +``` + +**Example 2: Any privileges required.** +Requires `` OR `` to access the route. +```ts +router.get({ + path: '/api/path', + security: { + authz: { + requiredPrivileges: [{ anyRequired: ['', ''] }], + }, + }, + ... +}, handler); +``` + +**Example 3: Complex configuration.** +Requires `` AND `` AND (`` OR ``) to access the route. +```ts +router.get({ + path: '/api/path', + security: { + authz: { + requiredPrivileges: [{ allRequired: ['', ''], anyRequired: ['', ''] }], + }, + }, + ... +}, handler); +``` + +### Versioned router security configuration examples +Different security configurations can be applied to each version when using the Versioned Router. This allows your authorization needs to evolve in lockstep with your API. + +**Example 1: Default and custom version security.** + +1. **Default configuration**: Applies to versions without specific authorization, requires ``. + +2. **Version 1**: Requires **both** `` and `` privileges. + +3. **Version 2**: Inherits the default authorization configuration, requiring ``. + +```ts +router.versioned + .get({ + path: '/internal/path', + access: 'internal', + // default security configuration, will be used for version unless overridden + security: { + authz: { + requiredPrivileges: [''], + }, + }, + }) + .addVersion({ + version: '1', + validate: false, + security: { + authz: { + requiredPrivileges: ['', ''], + }, + }, + }, handlerV1) + .addVersion({ + version: '2', + validate: false, + }, handlerV2); +``` + +**Example 2: Multiple versions with different security requirements.** +1. **Default Configuration**: Applies to versions without specific authorization, requires ``. + +2. **Version 1**: Requires **both** `` and `` privileges. + +3. **Version 2**: Requires `` AND (`` OR ``). + +4. **Version 3**: Requires only ``. + +```ts +router.versioned + .get({ + path: '/internal/path', + access: 'internal', + // default security configuration, will be used for version unless overridden + security: { + authz: { + requiredPrivileges: [''], + }, + }, + }) + .addVersion({ + version: '1', + validate: false, + security: { + authz: { + requiredPrivileges: ['', ''], + }, + }, + }, handlerV1) + .addVersion({ + version: '2', + validate: false, + security: { + authz: { + requiredPrivileges: ['', anyRequired: ['', '']], + }, + }, + }, handlerV2) + .addVersion({ + version: '3', + validate: false, + security: { + authz: { + requiredPrivileges: [''], + }, + }, + }, handlerV3); +``` + +## Authorization response available in route handlers +The `AuthzResult` object is available in route handlers, which provides information about the privileges granted to the caller. +For example, you have a route that requires `` and ANY of the privileges `` OR ``: +```ts +router.get({ + path: '/api/path', + security: { + authz: { + requiredPrivileges: ['', { anyRequired: ['', ''] }], + }, + }, + ... +}, (context, request, response) => { + // The authorization response is available in `request.authzResult` + // { + // "": true, + // "": true, + // "": false + // } +}); +``` + +## OpenAPI specification (OAS) documentation +Based on the security configuration defined in routes, OAS documentation will automatically generate and include description about the required privileges. +This makes it easy to view the security requirements of each endpoint in a standardized format, facilitating better understanding and usage by developers or teams consuming the API. + +To check the OAS documentation for a specific API route and see its security details, you can use the following command: +```sh +GET /api/oas?pathStartsWith=/your/api/path +``` + +## Migrating from `access` tags to `security` configuration +We aim to use the same privileges that are currently defined with tags `access:`. +To assist with this migration, we have created eslint rule `no_deprecated_authz_config`, that will automatically convert your `access` tags to the new `security` configuration. +It scans route definitions and converts `access` tags to the new `requiredPriviliges` configuration. + +Note: The rule is disabled by default to avoid automatic migrations without an oversight. You can perform migrations by running: + +**Migrate routes with defined authorization** +```sh +MIGRATE_DISABLED_AUTHZ=false MIGRATE_ENABLED_AUTHZ=true npx eslint --ext .ts --fix path/to/your/folder +``` + +**Migrate routes opted out from authorization** +```sh +MIGRATE_DISABLED_AUTHZ=true MIGRATE_ENABLED_AUTHZ=false npx eslint --ext .ts --fix path/to/your/folder +``` +We encourage you to migrate routes that are opted out from authorization to new config and provide legitimate reason for disabled authorization. +It is better to migrate routes opted out from authorization iteratively and elaborate on the reasoning. +Routes without a compelling reason to opt-out of authorization should plan to introduce them as soon as possible. + +**Migrate all routes** +```sh +MIGRATE_DISABLED_AUTHZ=true MIGRATE_ENABLED_AUTHZ=true npx eslint --ext .ts --fix path/to/your/folder +``` + +## Questions? +If you have any questions or need help with API authorization, please reach out to the `@elastic/kibana-security` team. + + diff --git a/dev_docs/key_concepts/security.mdx b/dev_docs/key_concepts/kibana_system_user.mdx similarity index 62% rename from dev_docs/key_concepts/security.mdx rename to dev_docs/key_concepts/kibana_system_user.mdx index 8e0bed133fe79..0373c8fa5d402 100644 --- a/dev_docs/key_concepts/security.mdx +++ b/dev_docs/key_concepts/kibana_system_user.mdx @@ -1,40 +1,12 @@ --- -id: kibDevDocsSecurityIntro -slug: /kibana-dev-docs/key-concepts/security-intro -title: Security -description: Maintaining Kibana's security posture -date: 2023-07-11 +id: kibDevDocsSecurityKibanaSystemUser +slug: /kibana-dev-docs/key-concepts/security-kibana-system-user +title: Security Kibana System User +description: This guide provides an overview of `kibana_system` user +date: 2024-10-04 tags: ['kibana', 'dev', 'contributor', 'security'] --- -Security is everyone's responsibility. This is inclusive of design, product, and engineering. The purpose of this guide is to give a high-level overview of security constructs and expectations. - -This guide covers the following topics: - -* [API authorization](#api-authorization) -* [The `kibana_system` user](#the-kibana_system-user) - -## API authorization -Kibana API routes do not have any authorization checks applied by default. This means that your APIs are accessible to anyone with valid credentials, regardless of their permissions. This includes users with no roles assigned. -This on its own is insufficient, and care must be taken to ensure that only authorized users can invoke your endpoints. - -### Adding API authorization -Kibana leverages for a majority of its persistence. The Saved Objects Service performs its own authorization checks, so if your API route is primarily a CRUD interface to Saved Objects, then your authorization needs are already met. -This is also true for derivatives of the Saved Objects Service, such as the Alerting and Cases services. - -If your endpoint is not a CRUD interface to Saved Objects, then your route should include `access` tags to ensure that only authorized users can invoke your endpoint. This is **especially** important if your route does any of the following: -1. Performs non-insignificant processing, causing load on the Kibana server. -2. Calls Elasticsearch using the internal `kibana_system` user. -3. Calls a third-party service. -4. Returns any non-public information to the caller, such as system configuration or state. - -More information on adding `access` tags to your routes can be found temporarily in the [legacy documentation](https://www.elastic.co/guide/en/kibana/current/development-security.html#development-plugin-feature-registration) - -### Why not add `access` tags to all routes by default? -Each authorization check that we perform involves a round-trip to Elasticsearch, so they are not as cheap as we'd like. Therefore, we want to keep the number of authorization checks we perform within a single route to a minimum. -Adding an `access` tag to routes that leverage the Saved Objects Service would be redundant in most cases, since the Saved Objects Service will be performing authorization anyway. - - ## The `kibana_system` user The Kibana server authenticates to Elasticsearch using the `elastic/kibana` [service account](https://www.elastic.co/guide/en/elasticsearch/reference/current/service-accounts.html#service-accounts-explanation). This service account has privileges that are equivilent to the `kibana_system` reserved role, whose descriptor is managed in the Elasticsearch repository ([source link](https://github.com/elastic/elasticsearch/blob/430cde6909eae12e1a90ac2bff29b71cbf4af18b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/KibanaOwnedReservedRoleDescriptors.java#L58)). diff --git a/dev_docs/lens/xy.mdx b/dev_docs/lens/xy.mdx index a53d7ec5a38e1..fc6fb4e6c15bf 100644 --- a/dev_docs/lens/xy.mdx +++ b/dev_docs/lens/xy.mdx @@ -46,7 +46,7 @@ Understanding `LensXYConfig` in detail ### `emphasizeFitting` - **Type:** `boolean` -- **Description:** When set to true, emphasizes the fitting of lines to the data points in line charts, making trends and patterns more apparent. +- **Description:** When set to true a straight line will be used between isolated points in a line chart, a dashed line will be used otherwise. ### `fittingFunction` diff --git a/dev_docs/nav-kibana-dev.docnav.json b/dev_docs/nav-kibana-dev.docnav.json index a7d696fc10574..6dd2ca052b7bd 100644 --- a/dev_docs/nav-kibana-dev.docnav.json +++ b/dev_docs/nav-kibana-dev.docnav.json @@ -101,7 +101,10 @@ "id": "kibBuildingBlocks" }, { - "id": "kibDevDocsSecurityIntro" + "id": "kibDevDocsSecurityAPIAuthorization" + }, + { + "id": "kibDevDocsSecurityKibanaSystemUser" }, { "id": "kibDevFeaturePrivileges", @@ -653,4 +656,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/docs/spaces/images/edit-space.png b/docs/spaces/images/edit-space.png deleted file mode 100644 index 97d7ec009ade4..0000000000000 Binary files a/docs/spaces/images/edit-space.png and /dev/null differ diff --git a/docs/spaces/images/space-management.png b/docs/spaces/images/space-management.png deleted file mode 100644 index bbb0164009e53..0000000000000 Binary files a/docs/spaces/images/space-management.png and /dev/null differ diff --git a/docs/spaces/images/spaces-roles.png b/docs/spaces/images/spaces-roles.png index b9003a91092bf..ed62b7f1bc7c9 100644 Binary files a/docs/spaces/images/spaces-roles.png and b/docs/spaces/images/spaces-roles.png differ diff --git a/docs/spaces/index.asciidoc b/docs/spaces/index.asciidoc index 9b20d1c23719b..81d19a2612cf6 100644 --- a/docs/spaces/index.asciidoc +++ b/docs/spaces/index.asciidoc @@ -2,87 +2,77 @@ [[xpack-spaces]] == Spaces -Spaces enable you to organize your dashboards and other saved -objects into meaningful categories. Once inside a space, you see only -the dashboards and saved objects that belong to that space. +You can define multiple spaces in a single {kib} instance from the **Spaces** menu. Each space has its own navigation and saved objects, and users can only access the spaces that they have been granted access to. This access is based on user roles, and a given role can have different permissions per space. {kib} creates a default space for you. -After you create your own -spaces, you're asked to choose a space when you log in to {kib}. You can change your -current space at any time by using the menu. +When you create more spaces, users are asked to choose a space when they log in to {kib}, and can change their +current space at any time from the top menu. [role="screenshot"] image::images/change-space.png["Change current space menu"] +To go to **Spaces**, find **Stack Management** in the navigation menu or use the <>. + [float] -==== Required privileges +=== Required privileges The `kibana_admin` role or equivalent is required to manage **Spaces**. [float] [[spaces-managing]] -=== View, create, and delete spaces - -Open the main menu, then click *Stack Management > Spaces* for an overview of your spaces. This view provides actions -for you to create, edit, and delete spaces. - -[role="screenshot"] -image::images/space-management.png["Space management"] +=== Create a space -[float] -==== Create or edit a space - -You can create as many spaces as you like. Click *Create a space* and provide a name, -URL identifier, optional description. +[[spaces-control-feature-visibility]] +You can have up to 100 spaces. +. Select *Create space* and provide a name, description, and URL identifier. ++ The URL identifier is a short text string that becomes part of the {kib} URL when you are inside that space. {kib} suggests a URL identifier based on the name of your space, but you can customize the identifier to your liking. You cannot change the space identifier once you create the space. -{kib} also has an <> -if you prefer to create spaces programmatically. +. Select a **Solution view**. This setting controls the navigation that all users of the space will get: -[role="screenshot"] -image::images/edit-space.png["Space management"] +** **Search**: A light navigation menu focused on analytics and Search use cases. Features specific to Observability and Security are hidden. +** **Observability**: A light navigation menu focused on analytics and Observability use cases. Features specific to Search and Security are hidden. +** **Security**: A light navigation menu focused on analytics and Security use cases. Features specific to Observability and Search are hidden. +** **Classic**: All features from all solutions are visible by default using the classic, multilayered navigation menus. You can customize which features are visible individually. -[float] -==== Delete a space - -Deleting a space permanently removes the space and all of its contents. -Find the space on the *Spaces* overview page and click the trash icon in the Actions column. -You can't delete the default space, but you can customize it to your liking. +. If you selected the **Classic** solution view, you can customize the **Feature visibility** as you need it to be for that space. ++ +NOTE: Even when disabled in this menu, some Management features can remain visible to some users depending on their privileges. Additionally, controlling feature visibility is not a security feature. To secure access +to specific features on a per-user basis, you must configure <>. -[float] -[[spaces-control-feature-visibility]] -=== Control feature access based on user needs +. Customize the avatar of the space to your liking. -You have control over which features are visible in each space. -For example, you might hide *Dev Tools* -in your "Executive" space or show *Stack Monitoring* only in your "Admin" space. -You can define which features to show or hide when you add or edit a space. +. Save your new space by selecting **Create space**. -Controlling feature -visibility is not a security feature. To secure access -to specific features on a per-user basis, you must configure -<>. +You can edit all of the space settings you just defined at any time, except for the URL identifier. -[role="screenshot"] -image::images/edit-space-feature-visibility.png["Controlling features visibility"] +{kib} also has an <> +if you prefer to create spaces programmatically. [float] [[spaces-control-user-access]] -=== Control feature access based on user privileges +=== Define access to a space -When using {kib} with security, you can configure applications and features -based on your users’ privileges. This means different roles can have access -to different features in the same space. -Power users might have privileges to create and edit visualizations and dashboards, -while analysts or executives might have read-only privileges for *Dashboard* and *Canvas*. -Refer to <> for details. +Users can access spaces based on the roles that they have. -[role="screenshot"] -image::images/spaces-roles.png["Controlling features visibility"] +* Certain reserved roles can view and access all spaces by default. You can't prevent those roles from accessing a space. Instead, you can grant different roles to your users. +* When <>, you can define which existing spaces that role can access, and with which permissions. +* When editing a space, you can assign roles to the space and define the permissions within the space for these roles. To do that, go to the **Permissions** tab of the space you're editing. ++ +When a role is assigned to _All Spaces_, you can't remove its access from the space settings. You must instead edit the role to give it more granular access to individual spaces. + +[float] +=== Delete a space + +Deleting a space permanently removes the space and all of its contents. +Find the space on the *Spaces* overview page and click the trash icon in the Actions column. +You can't delete the default space, but you can customize it to your liking. + +//[[spaces-control-feature-visibility]] [float] [[spaces-moving-objects]] @@ -107,6 +97,6 @@ image::images/spaces-configure-landing-page.png["Configure space-level landing p [float] [[spaces-delete-started]] -=== Disabling spaces +=== Disable spaces -Starting in {kib} 8.0, the Spaces feature cannot be disabled. +Since {kib} 8.0, the Spaces feature cannot be disabled. diff --git a/examples/routing_example/common/index.ts b/examples/routing_example/common/index.ts index c86a994f469f1..b83582b66ff08 100644 --- a/examples/routing_example/common/index.ts +++ b/examples/routing_example/common/index.ts @@ -15,3 +15,9 @@ export const POST_MESSAGE_ROUTE_PATH = '/api/post_message'; // Internal APIs should use the `internal` prefix, instead of the `api` prefix. export const INTERNAL_GET_MESSAGE_BY_ID_ROUTE = '/internal/get_message'; + +export const DEPRECATED_ROUTES = { + REMOVED_ROUTE: '/api/routing_example/d/removed_route', + MIGRATED_ROUTE: '/api/routing_example/d/migrated_route', + VERSIONED_ROUTE: '/api/routing_example/d/versioned', +}; diff --git a/examples/routing_example/server/plugin.ts b/examples/routing_example/server/plugin.ts index cb6a00920dd05..d4036afc58b5b 100644 --- a/examples/routing_example/server/plugin.ts +++ b/examples/routing_example/server/plugin.ts @@ -8,13 +8,14 @@ */ import { Plugin, CoreSetup, CoreStart } from '@kbn/core/server'; -import { registerRoutes } from './routes'; +import { registerRoutes, registerDeprecatedRoutes } from './routes'; export class RoutingExamplePlugin implements Plugin<{}, {}> { public setup(core: CoreSetup) { const router = core.http.createRouter(); registerRoutes(router); + registerDeprecatedRoutes(router); return {}; } diff --git a/examples/routing_example/server/routes/deprecated_routes/index.ts b/examples/routing_example/server/routes/deprecated_routes/index.ts new file mode 100644 index 0000000000000..75dc0261ed1b9 --- /dev/null +++ b/examples/routing_example/server/routes/deprecated_routes/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { IRouter } from '@kbn/core/server'; +import { registerDeprecatedRoute } from './unversioned'; +import { registerVersionedDeprecatedRoute } from './versioned'; + +export function registerDeprecatedRoutes(router: IRouter) { + registerDeprecatedRoute(router); + registerVersionedDeprecatedRoute(router); +} diff --git a/examples/routing_example/server/routes/deprecated_routes/unversioned.ts b/examples/routing_example/server/routes/deprecated_routes/unversioned.ts new file mode 100644 index 0000000000000..4e1451a91fc38 --- /dev/null +++ b/examples/routing_example/server/routes/deprecated_routes/unversioned.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import type { IRouter } from '@kbn/core/server'; +import { schema } from '@kbn/config-schema'; +import { DEPRECATED_ROUTES } from '../../../common'; + +export const registerDeprecatedRoute = (router: IRouter) => { + router.get( + { + path: DEPRECATED_ROUTES.REMOVED_ROUTE, + validate: false, + options: { + access: 'public', + deprecated: { + documentationUrl: 'https://elastic.co/', + severity: 'critical', + reason: { type: 'remove' }, + }, + }, + }, + async (ctx, req, res) => { + return res.ok({ + body: { result: 'Called deprecated route. Check UA to see the deprecation.' }, + }); + } + ); + + router.post( + { + path: DEPRECATED_ROUTES.MIGRATED_ROUTE, + validate: { + body: schema.object({ + test: schema.maybe(schema.boolean()), + }), + }, + options: { + access: 'public', + deprecated: { + documentationUrl: 'https://elastic.co/', + severity: 'critical', + reason: { + type: 'migrate', + newApiMethod: 'GET', + newApiPath: `${DEPRECATED_ROUTES.VERSIONED_ROUTE}?apiVersion=2`, + }, + }, + }, + }, + async (ctx, req, res) => { + return res.ok({ + body: { result: 'Called deprecated route. Check UA to see the deprecation.' }, + }); + } + ); +}; diff --git a/examples/routing_example/server/routes/deprecated_routes/versioned.ts b/examples/routing_example/server/routes/deprecated_routes/versioned.ts new file mode 100644 index 0000000000000..54d6f779f77c3 --- /dev/null +++ b/examples/routing_example/server/routes/deprecated_routes/versioned.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import type { RequestHandler } from '@kbn/core-http-server'; +import type { IRouter } from '@kbn/core/server'; +import { DEPRECATED_ROUTES } from '../../../common'; + +const createDummyHandler = + (version: string): RequestHandler => + (ctx, req, res) => { + return res.ok({ body: { result: `API version ${version}.` } }); + }; + +export const registerVersionedDeprecatedRoute = (router: IRouter) => { + const versionedRoute = router.versioned.get({ + path: DEPRECATED_ROUTES.VERSIONED_ROUTE, + description: 'Routing example plugin deprecated versioned route.', + access: 'internal', + options: { + excludeFromOAS: true, + }, + enableQueryVersion: true, + }); + + versionedRoute.addVersion( + { + options: { + deprecated: { + documentationUrl: 'https://elastic.co/', + severity: 'warning', + reason: { type: 'bump', newApiVersion: '2' }, + }, + }, + validate: false, + version: '1', + }, + createDummyHandler('1') + ); + + versionedRoute.addVersion( + { + version: '2', + validate: false, + }, + createDummyHandler('2') + ); +}; diff --git a/examples/routing_example/server/routes/index.ts b/examples/routing_example/server/routes/index.ts index 2f43c85b0d471..1c1ccb9e191ff 100644 --- a/examples/routing_example/server/routes/index.ts +++ b/examples/routing_example/server/routes/index.ts @@ -8,3 +8,4 @@ */ export { registerRoutes } from './register_routes'; +export { registerDeprecatedRoutes } from './deprecated_routes'; diff --git a/examples/routing_example/tsconfig.json b/examples/routing_example/tsconfig.json index b35e8dbd34f4a..86bfda9d3d529 100644 --- a/examples/routing_example/tsconfig.json +++ b/examples/routing_example/tsconfig.json @@ -20,5 +20,6 @@ "@kbn/core-http-browser", "@kbn/config-schema", "@kbn/react-kibana-context-render", + "@kbn/core-http-server", ] } diff --git a/oas_docs/bundle.json b/oas_docs/bundle.json index ca864dd0bcc63..6bca9024e77ea 100644 --- a/oas_docs/bundle.json +++ b/oas_docs/bundle.json @@ -6409,7 +6409,6 @@ }, "/api/fleet/agent-status": { "get": { - "deprecated": true, "operationId": "%2Fapi%2Ffleet%2Fagent-status#0", "parameters": [ { @@ -7121,14 +7120,26 @@ } }, { + "description": "use withAgentCount instead", "in": "query", "name": "noAgentCount", "required": false, "schema": { + "deprecated": true, "type": "boolean" } }, { + "description": "get policies with agent count", + "in": "query", + "name": "withAgentCount", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "description": "get full policies with package policies populated", "in": "query", "name": "full", "required": false, @@ -9764,6 +9775,191 @@ ] } }, + "/api/fleet/agent_policies/outputs": { + "post": { + "description": "Get list of outputs associated with agent policies", + "operationId": "%2Fapi%2Ffleet%2Fagent_policies%2Foutputs#0", + "parameters": [ + { + "description": "The version of the API to use", + "in": "header", + "name": "elastic-api-version", + "schema": { + "default": "2023-10-31", + "enum": [ + "2023-10-31" + ], + "type": "string" + } + }, + { + "description": "A required header to protect against CSRF attacks", + "in": "header", + "name": "kbn-xsrf", + "required": true, + "schema": { + "example": "true", + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json; Elastic-Api-Version=2023-10-31": { + "schema": { + "additionalProperties": false, + "properties": { + "ids": { + "description": "list of package policy ids", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "ids" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json; Elastic-Api-Version=2023-10-31": { + "schema": { + "additionalProperties": false, + "properties": { + "items": { + "items": { + "additionalProperties": false, + "properties": { + "agentPolicyId": { + "type": "string" + }, + "data": { + "additionalProperties": false, + "properties": { + "integrations": { + "items": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "integrationPolicyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "pkgName": { + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + }, + "output": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ], + "type": "object" + } + }, + "required": [ + "output" + ], + "type": "object" + }, + "monitoring": { + "additionalProperties": false, + "properties": { + "output": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ], + "type": "object" + } + }, + "required": [ + "output" + ], + "type": "object" + } + }, + "required": [ + "monitoring", + "data" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "items" + ], + "type": "object" + } + } + } + }, + "400": { + "content": { + "application/json; Elastic-Api-Version=2023-10-31": { + "schema": { + "additionalProperties": false, + "description": "Generic Error", + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "statusCode": { + "type": "number" + } + }, + "required": [ + "message" + ], + "type": "object" + } + } + } + } + }, + "summary": "", + "tags": [ + "Elastic Agent policies" + ] + } + }, "/api/fleet/agent_policies/{agentPolicyId}": { "get": { "description": "Get an agent policy by ID", @@ -12926,6 +13122,164 @@ ] } }, + "/api/fleet/agent_policies/{agentPolicyId}/outputs": { + "get": { + "description": "Get list of outputs associated with agent policy by policy id", + "operationId": "%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Foutputs#0", + "parameters": [ + { + "description": "The version of the API to use", + "in": "header", + "name": "elastic-api-version", + "schema": { + "default": "2023-10-31", + "enum": [ + "2023-10-31" + ], + "type": "string" + } + }, + { + "in": "path", + "name": "agentPolicyId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json; Elastic-Api-Version=2023-10-31": { + "schema": { + "additionalProperties": false, + "properties": { + "item": { + "additionalProperties": false, + "properties": { + "agentPolicyId": { + "type": "string" + }, + "data": { + "additionalProperties": false, + "properties": { + "integrations": { + "items": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "integrationPolicyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "pkgName": { + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + }, + "output": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ], + "type": "object" + } + }, + "required": [ + "output" + ], + "type": "object" + }, + "monitoring": { + "additionalProperties": false, + "properties": { + "output": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ], + "type": "object" + } + }, + "required": [ + "output" + ], + "type": "object" + } + }, + "required": [ + "monitoring", + "data" + ], + "type": "object" + } + }, + "required": [ + "item" + ], + "type": "object" + } + } + } + }, + "400": { + "content": { + "application/json; Elastic-Api-Version=2023-10-31": { + "schema": { + "additionalProperties": false, + "description": "Generic Error", + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "statusCode": { + "type": "number" + } + }, + "required": [ + "message" + ], + "type": "object" + } + } + } + } + }, + "summary": "", + "tags": [ + "Elastic Agent policies" + ] + } + }, "/api/fleet/agent_status": { "get": { "description": "Get agent status summary", @@ -17124,7 +17478,6 @@ ] }, "put": { - "deprecated": true, "operationId": "%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Freassign#0", "parameters": [ { @@ -17824,7 +18177,6 @@ }, "/api/fleet/enrollment-api-keys": { "get": { - "deprecated": true, "operationId": "%2Fapi%2Ffleet%2Fenrollment-api-keys#0", "parameters": [ { @@ -17871,7 +18223,6 @@ "tags": [] }, "post": { - "deprecated": true, "operationId": "%2Fapi%2Ffleet%2Fenrollment-api-keys#1", "parameters": [ { @@ -17928,7 +18279,6 @@ }, "/api/fleet/enrollment-api-keys/{keyId}": { "delete": { - "deprecated": true, "operationId": "%2Fapi%2Ffleet%2Fenrollment-api-keys%2F%7BkeyId%7D#1", "parameters": [ { @@ -17967,7 +18317,6 @@ "tags": [] }, "get": { - "deprecated": true, "operationId": "%2Fapi%2Ffleet%2Fenrollment-api-keys%2F%7BkeyId%7D#0", "parameters": [ { @@ -24698,7 +25047,6 @@ }, "/api/fleet/epm/packages/{pkgkey}": { "delete": { - "deprecated": true, "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#3", "parameters": [ { @@ -24756,7 +25104,6 @@ "tags": [] }, "get": { - "deprecated": true, "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#0", "parameters": [ { @@ -24818,7 +25165,6 @@ "tags": [] }, "post": { - "deprecated": true, "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#2", "parameters": [ { @@ -24902,7 +25248,6 @@ "tags": [] }, "put": { - "deprecated": true, "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#1", "parameters": [ { @@ -40117,7 +40462,6 @@ }, "/api/fleet/service-tokens": { "post": { - "deprecated": true, "description": "Create a service token", "operationId": "%2Fapi%2Ffleet%2Fservice-tokens#0", "parameters": [ diff --git a/oas_docs/bundle.serverless.json b/oas_docs/bundle.serverless.json index fd41029331181..a8d428d5404fc 100644 --- a/oas_docs/bundle.serverless.json +++ b/oas_docs/bundle.serverless.json @@ -6409,7 +6409,6 @@ }, "/api/fleet/agent-status": { "get": { - "deprecated": true, "operationId": "%2Fapi%2Ffleet%2Fagent-status#0", "parameters": [ { @@ -7121,14 +7120,26 @@ } }, { + "description": "use withAgentCount instead", "in": "query", "name": "noAgentCount", "required": false, "schema": { + "deprecated": true, "type": "boolean" } }, { + "description": "get policies with agent count", + "in": "query", + "name": "withAgentCount", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "description": "get full policies with package policies populated", "in": "query", "name": "full", "required": false, @@ -9764,6 +9775,191 @@ ] } }, + "/api/fleet/agent_policies/outputs": { + "post": { + "description": "Get list of outputs associated with agent policies", + "operationId": "%2Fapi%2Ffleet%2Fagent_policies%2Foutputs#0", + "parameters": [ + { + "description": "The version of the API to use", + "in": "header", + "name": "elastic-api-version", + "schema": { + "default": "2023-10-31", + "enum": [ + "2023-10-31" + ], + "type": "string" + } + }, + { + "description": "A required header to protect against CSRF attacks", + "in": "header", + "name": "kbn-xsrf", + "required": true, + "schema": { + "example": "true", + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json; Elastic-Api-Version=2023-10-31": { + "schema": { + "additionalProperties": false, + "properties": { + "ids": { + "description": "list of package policy ids", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "ids" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json; Elastic-Api-Version=2023-10-31": { + "schema": { + "additionalProperties": false, + "properties": { + "items": { + "items": { + "additionalProperties": false, + "properties": { + "agentPolicyId": { + "type": "string" + }, + "data": { + "additionalProperties": false, + "properties": { + "integrations": { + "items": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "integrationPolicyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "pkgName": { + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + }, + "output": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ], + "type": "object" + } + }, + "required": [ + "output" + ], + "type": "object" + }, + "monitoring": { + "additionalProperties": false, + "properties": { + "output": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ], + "type": "object" + } + }, + "required": [ + "output" + ], + "type": "object" + } + }, + "required": [ + "monitoring", + "data" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "items" + ], + "type": "object" + } + } + } + }, + "400": { + "content": { + "application/json; Elastic-Api-Version=2023-10-31": { + "schema": { + "additionalProperties": false, + "description": "Generic Error", + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "statusCode": { + "type": "number" + } + }, + "required": [ + "message" + ], + "type": "object" + } + } + } + } + }, + "summary": "", + "tags": [ + "Elastic Agent policies" + ] + } + }, "/api/fleet/agent_policies/{agentPolicyId}": { "get": { "description": "Get an agent policy by ID", @@ -12926,6 +13122,164 @@ ] } }, + "/api/fleet/agent_policies/{agentPolicyId}/outputs": { + "get": { + "description": "Get list of outputs associated with agent policy by policy id", + "operationId": "%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Foutputs#0", + "parameters": [ + { + "description": "The version of the API to use", + "in": "header", + "name": "elastic-api-version", + "schema": { + "default": "2023-10-31", + "enum": [ + "2023-10-31" + ], + "type": "string" + } + }, + { + "in": "path", + "name": "agentPolicyId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json; Elastic-Api-Version=2023-10-31": { + "schema": { + "additionalProperties": false, + "properties": { + "item": { + "additionalProperties": false, + "properties": { + "agentPolicyId": { + "type": "string" + }, + "data": { + "additionalProperties": false, + "properties": { + "integrations": { + "items": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "integrationPolicyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "pkgName": { + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + }, + "output": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ], + "type": "object" + } + }, + "required": [ + "output" + ], + "type": "object" + }, + "monitoring": { + "additionalProperties": false, + "properties": { + "output": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ], + "type": "object" + } + }, + "required": [ + "output" + ], + "type": "object" + } + }, + "required": [ + "monitoring", + "data" + ], + "type": "object" + } + }, + "required": [ + "item" + ], + "type": "object" + } + } + } + }, + "400": { + "content": { + "application/json; Elastic-Api-Version=2023-10-31": { + "schema": { + "additionalProperties": false, + "description": "Generic Error", + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "statusCode": { + "type": "number" + } + }, + "required": [ + "message" + ], + "type": "object" + } + } + } + } + }, + "summary": "", + "tags": [ + "Elastic Agent policies" + ] + } + }, "/api/fleet/agent_status": { "get": { "description": "Get agent status summary", @@ -17124,7 +17478,6 @@ ] }, "put": { - "deprecated": true, "operationId": "%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Freassign#0", "parameters": [ { @@ -17824,7 +18177,6 @@ }, "/api/fleet/enrollment-api-keys": { "get": { - "deprecated": true, "operationId": "%2Fapi%2Ffleet%2Fenrollment-api-keys#0", "parameters": [ { @@ -17871,7 +18223,6 @@ "tags": [] }, "post": { - "deprecated": true, "operationId": "%2Fapi%2Ffleet%2Fenrollment-api-keys#1", "parameters": [ { @@ -17928,7 +18279,6 @@ }, "/api/fleet/enrollment-api-keys/{keyId}": { "delete": { - "deprecated": true, "operationId": "%2Fapi%2Ffleet%2Fenrollment-api-keys%2F%7BkeyId%7D#1", "parameters": [ { @@ -17967,7 +18317,6 @@ "tags": [] }, "get": { - "deprecated": true, "operationId": "%2Fapi%2Ffleet%2Fenrollment-api-keys%2F%7BkeyId%7D#0", "parameters": [ { @@ -24698,7 +25047,6 @@ }, "/api/fleet/epm/packages/{pkgkey}": { "delete": { - "deprecated": true, "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#3", "parameters": [ { @@ -24756,7 +25104,6 @@ "tags": [] }, "get": { - "deprecated": true, "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#0", "parameters": [ { @@ -24818,7 +25165,6 @@ "tags": [] }, "post": { - "deprecated": true, "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#2", "parameters": [ { @@ -24902,7 +25248,6 @@ "tags": [] }, "put": { - "deprecated": true, "operationId": "%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#1", "parameters": [ { @@ -40117,7 +40462,6 @@ }, "/api/fleet/service-tokens": { "post": { - "deprecated": true, "description": "Create a service token", "operationId": "%2Fapi%2Ffleet%2Fservice-tokens#0", "parameters": [ @@ -40993,6 +41337,697 @@ ] } }, + "/api/security/role": { + "get": { + "operationId": "%2Fapi%2Fsecurity%2Frole#0", + "parameters": [ + { + "description": "The version of the API to use", + "in": "header", + "name": "elastic-api-version", + "schema": { + "default": "2023-10-31", + "enum": [ + "2023-10-31" + ], + "type": "string" + } + }, + { + "in": "query", + "name": "replaceDeprecatedPrivileges", + "required": false, + "schema": { + "type": "boolean" + } + } + ], + "responses": {}, + "summary": "Get all roles", + "tags": [ + "roles" + ] + } + }, + "/api/security/role/{name}": { + "delete": { + "operationId": "%2Fapi%2Fsecurity%2Frole%2F%7Bname%7D#1", + "parameters": [ + { + "description": "The version of the API to use", + "in": "header", + "name": "elastic-api-version", + "schema": { + "default": "2023-10-31", + "enum": [ + "2023-10-31" + ], + "type": "string" + } + }, + { + "description": "A required header to protect against CSRF attacks", + "in": "header", + "name": "kbn-xsrf", + "required": true, + "schema": { + "example": "true", + "type": "string" + } + }, + { + "in": "path", + "name": "name", + "required": true, + "schema": { + "minLength": 1, + "type": "string" + } + } + ], + "responses": {}, + "summary": "Delete a role", + "tags": [ + "roles" + ] + }, + "get": { + "operationId": "%2Fapi%2Fsecurity%2Frole%2F%7Bname%7D#0", + "parameters": [ + { + "description": "The version of the API to use", + "in": "header", + "name": "elastic-api-version", + "schema": { + "default": "2023-10-31", + "enum": [ + "2023-10-31" + ], + "type": "string" + } + }, + { + "in": "path", + "name": "name", + "required": true, + "schema": { + "minLength": 1, + "type": "string" + } + }, + { + "in": "query", + "name": "replaceDeprecatedPrivileges", + "required": false, + "schema": { + "type": "boolean" + } + } + ], + "responses": {}, + "summary": "Get a role", + "tags": [ + "roles" + ] + }, + "put": { + "operationId": "%2Fapi%2Fsecurity%2Frole%2F%7Bname%7D#2", + "parameters": [ + { + "description": "The version of the API to use", + "in": "header", + "name": "elastic-api-version", + "schema": { + "default": "2023-10-31", + "enum": [ + "2023-10-31" + ], + "type": "string" + } + }, + { + "description": "A required header to protect against CSRF attacks", + "in": "header", + "name": "kbn-xsrf", + "required": true, + "schema": { + "example": "true", + "type": "string" + } + }, + { + "in": "path", + "name": "name", + "required": true, + "schema": { + "maxLength": 1024, + "minLength": 1, + "type": "string" + } + }, + { + "in": "query", + "name": "createOnly", + "required": false, + "schema": { + "default": false, + "type": "boolean" + } + } + ], + "requestBody": { + "content": { + "application/json; Elastic-Api-Version=2023-10-31": { + "schema": { + "additionalProperties": false, + "properties": { + "description": { + "maxLength": 2048, + "type": "string" + }, + "elasticsearch": { + "additionalProperties": false, + "properties": { + "cluster": { + "items": { + "type": "string" + }, + "type": "array" + }, + "indices": { + "items": { + "additionalProperties": false, + "properties": { + "allow_restricted_indices": { + "type": "boolean" + }, + "field_security": { + "additionalProperties": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": "object" + }, + "names": { + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "privileges": { + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "query": { + "type": "string" + } + }, + "required": [ + "names", + "privileges" + ], + "type": "object" + }, + "type": "array" + }, + "remote_cluster": { + "items": { + "additionalProperties": false, + "properties": { + "clusters": { + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "privileges": { + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array" + } + }, + "required": [ + "privileges", + "clusters" + ], + "type": "object" + }, + "type": "array" + }, + "remote_indices": { + "items": { + "additionalProperties": false, + "properties": { + "allow_restricted_indices": { + "type": "boolean" + }, + "clusters": { + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "field_security": { + "additionalProperties": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": "object" + }, + "names": { + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "privileges": { + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "query": { + "type": "string" + } + }, + "required": [ + "clusters", + "names", + "privileges" + ], + "type": "object" + }, + "type": "array" + }, + "run_as": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "kibana": { + "items": { + "additionalProperties": false, + "properties": { + "base": { + "anyOf": [ + { + "items": {}, + "type": "array" + }, + { + "type": "boolean" + }, + { + "type": "number" + }, + { + "type": "object" + }, + { + "type": "string" + } + ], + "nullable": true, + "oneOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ] + }, + "feature": { + "additionalProperties": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": "object" + }, + "spaces": { + "anyOf": [ + { + "items": { + "enum": [ + "*" + ], + "type": "string" + }, + "maxItems": 1, + "minItems": 1, + "type": "array" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": [ + "*" + ] + } + }, + "required": [ + "base" + ], + "type": "object" + }, + "type": "array" + }, + "metadata": { + "additionalProperties": {}, + "type": "object" + } + }, + "required": [ + "elasticsearch" + ], + "type": "object" + } + } + } + }, + "responses": {}, + "summary": "Create or update a role", + "tags": [ + "roles" + ] + } + }, + "/api/security/roles": { + "post": { + "operationId": "%2Fapi%2Fsecurity%2Froles#0", + "parameters": [ + { + "description": "The version of the API to use", + "in": "header", + "name": "elastic-api-version", + "schema": { + "default": "2023-10-31", + "enum": [ + "2023-10-31" + ], + "type": "string" + } + }, + { + "description": "A required header to protect against CSRF attacks", + "in": "header", + "name": "kbn-xsrf", + "required": true, + "schema": { + "example": "true", + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json; Elastic-Api-Version=2023-10-31": { + "schema": { + "additionalProperties": false, + "properties": { + "roles": { + "additionalProperties": { + "additionalProperties": false, + "properties": { + "description": { + "maxLength": 2048, + "type": "string" + }, + "elasticsearch": { + "additionalProperties": false, + "properties": { + "cluster": { + "items": { + "type": "string" + }, + "type": "array" + }, + "indices": { + "items": { + "additionalProperties": false, + "properties": { + "allow_restricted_indices": { + "type": "boolean" + }, + "field_security": { + "additionalProperties": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": "object" + }, + "names": { + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "privileges": { + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "query": { + "type": "string" + } + }, + "required": [ + "names", + "privileges" + ], + "type": "object" + }, + "type": "array" + }, + "remote_cluster": { + "items": { + "additionalProperties": false, + "properties": { + "clusters": { + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "privileges": { + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array" + } + }, + "required": [ + "privileges", + "clusters" + ], + "type": "object" + }, + "type": "array" + }, + "remote_indices": { + "items": { + "additionalProperties": false, + "properties": { + "allow_restricted_indices": { + "type": "boolean" + }, + "clusters": { + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "field_security": { + "additionalProperties": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": "object" + }, + "names": { + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "privileges": { + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "query": { + "type": "string" + } + }, + "required": [ + "clusters", + "names", + "privileges" + ], + "type": "object" + }, + "type": "array" + }, + "run_as": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "kibana": { + "items": { + "additionalProperties": false, + "properties": { + "base": { + "anyOf": [ + { + "items": {}, + "type": "array" + }, + { + "type": "boolean" + }, + { + "type": "number" + }, + { + "type": "object" + }, + { + "type": "string" + } + ], + "nullable": true, + "oneOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ] + }, + "feature": { + "additionalProperties": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": "object" + }, + "spaces": { + "anyOf": [ + { + "items": { + "enum": [ + "*" + ], + "type": "string" + }, + "maxItems": 1, + "minItems": 1, + "type": "array" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": [ + "*" + ] + } + }, + "required": [ + "base" + ], + "type": "object" + }, + "type": "array" + }, + "metadata": { + "additionalProperties": {}, + "type": "object" + } + }, + "required": [ + "elasticsearch" + ], + "type": "object" + }, + "type": "object" + } + }, + "required": [ + "roles" + ], + "type": "object" + } + } + } + }, + "responses": {}, + "summary": "Create or update roles", + "tags": [ + "roles" + ] + } + }, "/api/spaces/space": { "get": { "operationId": "%2Fapi%2Fspaces%2Fspace#0", @@ -41493,6 +42528,9 @@ { "name": "Message Signing Service" }, + { + "name": "roles" + }, { "name": "spaces" }, diff --git a/oas_docs/output/kibana.serverless.staging.yaml b/oas_docs/output/kibana.serverless.staging.yaml index acbbdd7bfca43..05f614ede3df7 100644 --- a/oas_docs/output/kibana.serverless.staging.yaml +++ b/oas_docs/output/kibana.serverless.staging.yaml @@ -10428,12 +10428,21 @@ paths: required: false schema: type: string - - in: query + - description: use withAgentCount instead + in: query name: noAgentCount required: false schema: + deprecated: true type: boolean - - in: query + - description: get policies with agent count + in: query + name: withAgentCount + required: false + schema: + type: boolean + - description: get full policies with package policies populated + in: query name: full required: false schema: @@ -14585,6 +14594,110 @@ paths: summary: '' tags: - Elastic Agent policies + /api/fleet/agent_policies/{agentPolicyId}/outputs: + get: + description: Get list of outputs associated with agent policy by policy id + operationId: '%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Foutputs#0' + parameters: + - description: The version of the API to use + in: header + name: elastic-api-version + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + - in: path + name: agentPolicyId + required: true + schema: + type: string + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + item: + additionalProperties: false + type: object + properties: + agentPolicyId: + type: string + data: + additionalProperties: false + type: object + properties: + integrations: + items: + additionalProperties: false + type: object + properties: + id: + type: string + integrationPolicyName: + type: string + name: + type: string + pkgName: + type: string + type: array + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + monitoring: + additionalProperties: false + type: object + properties: + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + required: + - monitoring + - data + required: + - item + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + description: Generic Error + type: object + properties: + error: + type: string + message: + type: string + statusCode: + type: number + required: + - message + summary: '' + tags: + - Elastic Agent policies /api/fleet/agent_policies/delete: post: description: Delete agent policy by ID @@ -14655,6 +14768,128 @@ paths: summary: '' tags: - Elastic Agent policies + /api/fleet/agent_policies/outputs: + post: + description: Get list of outputs associated with agent policies + operationId: '%2Fapi%2Ffleet%2Fagent_policies%2Foutputs#0' + parameters: + - description: The version of the API to use + in: header + name: elastic-api-version + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + - description: A required header to protect against CSRF attacks + in: header + name: kbn-xsrf + required: true + schema: + example: 'true' + type: string + requestBody: + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + ids: + description: list of package policy ids + items: + type: string + type: array + required: + - ids + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + items: + items: + additionalProperties: false + type: object + properties: + agentPolicyId: + type: string + data: + additionalProperties: false + type: object + properties: + integrations: + items: + additionalProperties: false + type: object + properties: + id: + type: string + integrationPolicyName: + type: string + name: + type: string + pkgName: + type: string + type: array + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + monitoring: + additionalProperties: false + type: object + properties: + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + required: + - monitoring + - data + type: array + required: + - items + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + description: Generic Error + type: object + properties: + error: + type: string + message: + type: string + statusCode: + type: number + required: + - message + summary: '' + tags: + - Elastic Agent policies /api/fleet/agent_status: get: description: Get agent status summary @@ -14831,7 +15066,6 @@ paths: - Elastic Agents /api/fleet/agent-status: get: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fagent-status#0' parameters: - description: The version of the API to use @@ -16534,7 +16768,6 @@ paths: tags: - Elastic Agent actions put: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Freassign#0' parameters: - description: The version of the API to use @@ -18387,7 +18620,6 @@ paths: - Fleet enrollment API keys /api/fleet/enrollment-api-keys: get: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fenrollment-api-keys#0' parameters: - description: The version of the API to use @@ -18419,7 +18651,6 @@ paths: summary: '' tags: [] post: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fenrollment-api-keys#1' parameters: - description: The version of the API to use @@ -18457,7 +18688,6 @@ paths: tags: [] /api/fleet/enrollment-api-keys/{keyId}: delete: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fenrollment-api-keys%2F%7BkeyId%7D#1' parameters: - description: The version of the API to use @@ -18484,7 +18714,6 @@ paths: summary: '' tags: [] get: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fenrollment-api-keys%2F%7BkeyId%7D#0' parameters: - description: The version of the API to use @@ -20199,7 +20428,6 @@ paths: - Elastic Package Manager (EPM) /api/fleet/epm/packages/{pkgkey}: delete: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#3' parameters: - description: The version of the API to use @@ -20238,7 +20466,6 @@ paths: summary: '' tags: [] get: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#0' parameters: - description: The version of the API to use @@ -20279,7 +20506,6 @@ paths: summary: '' tags: [] post: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#2' parameters: - description: The version of the API to use @@ -20335,7 +20561,6 @@ paths: summary: '' tags: [] put: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#1' parameters: - description: The version of the API to use @@ -33298,7 +33523,6 @@ paths: - Fleet service tokens /api/fleet/service-tokens: post: - deprecated: true description: Create a service token operationId: '%2Fapi%2Ffleet%2Fservice-tokens#0' parameters: @@ -36556,6 +36780,456 @@ paths: tags: - Security AI Assistant API - Prompts API + /api/security/role: + get: + operationId: '%2Fapi%2Fsecurity%2Frole#0' + parameters: + - description: The version of the API to use + in: header + name: elastic-api-version + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + - in: query + name: replaceDeprecatedPrivileges + required: false + schema: + type: boolean + responses: {} + summary: Get all roles + tags: + - roles + /api/security/role/{name}: + delete: + operationId: '%2Fapi%2Fsecurity%2Frole%2F%7Bname%7D#1' + parameters: + - description: The version of the API to use + in: header + name: elastic-api-version + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + - description: A required header to protect against CSRF attacks + in: header + name: kbn-xsrf + required: true + schema: + example: 'true' + type: string + - in: path + name: name + required: true + schema: + minLength: 1 + type: string + responses: {} + summary: Delete a role + tags: + - roles + get: + operationId: '%2Fapi%2Fsecurity%2Frole%2F%7Bname%7D#0' + parameters: + - description: The version of the API to use + in: header + name: elastic-api-version + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + - in: path + name: name + required: true + schema: + minLength: 1 + type: string + - in: query + name: replaceDeprecatedPrivileges + required: false + schema: + type: boolean + responses: {} + summary: Get a role + tags: + - roles + put: + operationId: '%2Fapi%2Fsecurity%2Frole%2F%7Bname%7D#2' + parameters: + - description: The version of the API to use + in: header + name: elastic-api-version + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + - description: A required header to protect against CSRF attacks + in: header + name: kbn-xsrf + required: true + schema: + example: 'true' + type: string + - in: path + name: name + required: true + schema: + maxLength: 1024 + minLength: 1 + type: string + - in: query + name: createOnly + required: false + schema: + default: false + type: boolean + requestBody: + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + description: + maxLength: 2048 + type: string + elasticsearch: + additionalProperties: false + type: object + properties: + cluster: + items: + type: string + type: array + indices: + items: + additionalProperties: false + type: object + properties: + allow_restricted_indices: + type: boolean + field_security: + additionalProperties: + items: + type: string + type: array + type: object + names: + items: + type: string + minItems: 1 + type: array + privileges: + items: + type: string + minItems: 1 + type: array + query: + type: string + required: + - names + - privileges + type: array + remote_cluster: + items: + additionalProperties: false + type: object + properties: + clusters: + items: + type: string + minItems: 1 + type: array + privileges: + items: + type: string + minItems: 1 + type: array + required: + - privileges + - clusters + type: array + remote_indices: + items: + additionalProperties: false + type: object + properties: + allow_restricted_indices: + type: boolean + clusters: + items: + type: string + minItems: 1 + type: array + field_security: + additionalProperties: + items: + type: string + type: array + type: object + names: + items: + type: string + minItems: 1 + type: array + privileges: + items: + type: string + minItems: 1 + type: array + query: + type: string + required: + - clusters + - names + - privileges + type: array + run_as: + items: + type: string + type: array + kibana: + items: + additionalProperties: false + type: object + properties: + base: + anyOf: + - items: {} + type: array + - type: boolean + - type: number + - type: object + - type: string + nullable: true + oneOf: + - items: + type: string + type: array + - items: + type: string + type: array + feature: + additionalProperties: + items: + type: string + type: array + type: object + spaces: + anyOf: + - items: + enum: + - '*' + type: string + maxItems: 1 + minItems: 1 + type: array + - items: + type: string + type: array + default: + - '*' + required: + - base + type: array + metadata: + additionalProperties: {} + type: object + required: + - elasticsearch + responses: {} + summary: Create or update a role + tags: + - roles + /api/security/roles: + post: + operationId: '%2Fapi%2Fsecurity%2Froles#0' + parameters: + - description: The version of the API to use + in: header + name: elastic-api-version + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + - description: A required header to protect against CSRF attacks + in: header + name: kbn-xsrf + required: true + schema: + example: 'true' + type: string + requestBody: + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + roles: + additionalProperties: + additionalProperties: false + type: object + properties: + description: + maxLength: 2048 + type: string + elasticsearch: + additionalProperties: false + type: object + properties: + cluster: + items: + type: string + type: array + indices: + items: + additionalProperties: false + type: object + properties: + allow_restricted_indices: + type: boolean + field_security: + additionalProperties: + items: + type: string + type: array + type: object + names: + items: + type: string + minItems: 1 + type: array + privileges: + items: + type: string + minItems: 1 + type: array + query: + type: string + required: + - names + - privileges + type: array + remote_cluster: + items: + additionalProperties: false + type: object + properties: + clusters: + items: + type: string + minItems: 1 + type: array + privileges: + items: + type: string + minItems: 1 + type: array + required: + - privileges + - clusters + type: array + remote_indices: + items: + additionalProperties: false + type: object + properties: + allow_restricted_indices: + type: boolean + clusters: + items: + type: string + minItems: 1 + type: array + field_security: + additionalProperties: + items: + type: string + type: array + type: object + names: + items: + type: string + minItems: 1 + type: array + privileges: + items: + type: string + minItems: 1 + type: array + query: + type: string + required: + - clusters + - names + - privileges + type: array + run_as: + items: + type: string + type: array + kibana: + items: + additionalProperties: false + type: object + properties: + base: + anyOf: + - items: {} + type: array + - type: boolean + - type: number + - type: object + - type: string + nullable: true + oneOf: + - items: + type: string + type: array + - items: + type: string + type: array + feature: + additionalProperties: + items: + type: string + type: array + type: object + spaces: + anyOf: + - items: + enum: + - '*' + type: string + maxItems: 1 + minItems: 1 + type: array + - items: + type: string + type: array + default: + - '*' + required: + - base + type: array + metadata: + additionalProperties: {} + type: object + required: + - elasticsearch + type: object + required: + - roles + responses: {} + summary: Create or update roles + tags: + - roles /api/spaces/space: get: operationId: '%2Fapi%2Fspaces%2Fspace#0' @@ -48211,9 +48885,7 @@ components: name: type: string source: - items: - type: string - type: array + type: string required: - name - source @@ -48365,9 +49037,7 @@ components: name: type: string source: - items: - type: string - type: array + type: string required: - name - source @@ -51504,6 +52174,7 @@ tags: - name: Message Signing Service - description: Machine learning name: ml + - name: roles - description: > Export sets of saved objects that you want to import into {kib}, resolve import errors, and rotate an encryption key for encrypted saved objects diff --git a/oas_docs/output/kibana.serverless.yaml b/oas_docs/output/kibana.serverless.yaml index acbbdd7bfca43..05f614ede3df7 100644 --- a/oas_docs/output/kibana.serverless.yaml +++ b/oas_docs/output/kibana.serverless.yaml @@ -10428,12 +10428,21 @@ paths: required: false schema: type: string - - in: query + - description: use withAgentCount instead + in: query name: noAgentCount required: false schema: + deprecated: true type: boolean - - in: query + - description: get policies with agent count + in: query + name: withAgentCount + required: false + schema: + type: boolean + - description: get full policies with package policies populated + in: query name: full required: false schema: @@ -14585,6 +14594,110 @@ paths: summary: '' tags: - Elastic Agent policies + /api/fleet/agent_policies/{agentPolicyId}/outputs: + get: + description: Get list of outputs associated with agent policy by policy id + operationId: '%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Foutputs#0' + parameters: + - description: The version of the API to use + in: header + name: elastic-api-version + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + - in: path + name: agentPolicyId + required: true + schema: + type: string + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + item: + additionalProperties: false + type: object + properties: + agentPolicyId: + type: string + data: + additionalProperties: false + type: object + properties: + integrations: + items: + additionalProperties: false + type: object + properties: + id: + type: string + integrationPolicyName: + type: string + name: + type: string + pkgName: + type: string + type: array + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + monitoring: + additionalProperties: false + type: object + properties: + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + required: + - monitoring + - data + required: + - item + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + description: Generic Error + type: object + properties: + error: + type: string + message: + type: string + statusCode: + type: number + required: + - message + summary: '' + tags: + - Elastic Agent policies /api/fleet/agent_policies/delete: post: description: Delete agent policy by ID @@ -14655,6 +14768,128 @@ paths: summary: '' tags: - Elastic Agent policies + /api/fleet/agent_policies/outputs: + post: + description: Get list of outputs associated with agent policies + operationId: '%2Fapi%2Ffleet%2Fagent_policies%2Foutputs#0' + parameters: + - description: The version of the API to use + in: header + name: elastic-api-version + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + - description: A required header to protect against CSRF attacks + in: header + name: kbn-xsrf + required: true + schema: + example: 'true' + type: string + requestBody: + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + ids: + description: list of package policy ids + items: + type: string + type: array + required: + - ids + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + items: + items: + additionalProperties: false + type: object + properties: + agentPolicyId: + type: string + data: + additionalProperties: false + type: object + properties: + integrations: + items: + additionalProperties: false + type: object + properties: + id: + type: string + integrationPolicyName: + type: string + name: + type: string + pkgName: + type: string + type: array + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + monitoring: + additionalProperties: false + type: object + properties: + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + required: + - monitoring + - data + type: array + required: + - items + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + description: Generic Error + type: object + properties: + error: + type: string + message: + type: string + statusCode: + type: number + required: + - message + summary: '' + tags: + - Elastic Agent policies /api/fleet/agent_status: get: description: Get agent status summary @@ -14831,7 +15066,6 @@ paths: - Elastic Agents /api/fleet/agent-status: get: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fagent-status#0' parameters: - description: The version of the API to use @@ -16534,7 +16768,6 @@ paths: tags: - Elastic Agent actions put: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Freassign#0' parameters: - description: The version of the API to use @@ -18387,7 +18620,6 @@ paths: - Fleet enrollment API keys /api/fleet/enrollment-api-keys: get: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fenrollment-api-keys#0' parameters: - description: The version of the API to use @@ -18419,7 +18651,6 @@ paths: summary: '' tags: [] post: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fenrollment-api-keys#1' parameters: - description: The version of the API to use @@ -18457,7 +18688,6 @@ paths: tags: [] /api/fleet/enrollment-api-keys/{keyId}: delete: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fenrollment-api-keys%2F%7BkeyId%7D#1' parameters: - description: The version of the API to use @@ -18484,7 +18714,6 @@ paths: summary: '' tags: [] get: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fenrollment-api-keys%2F%7BkeyId%7D#0' parameters: - description: The version of the API to use @@ -20199,7 +20428,6 @@ paths: - Elastic Package Manager (EPM) /api/fleet/epm/packages/{pkgkey}: delete: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#3' parameters: - description: The version of the API to use @@ -20238,7 +20466,6 @@ paths: summary: '' tags: [] get: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#0' parameters: - description: The version of the API to use @@ -20279,7 +20506,6 @@ paths: summary: '' tags: [] post: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#2' parameters: - description: The version of the API to use @@ -20335,7 +20561,6 @@ paths: summary: '' tags: [] put: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#1' parameters: - description: The version of the API to use @@ -33298,7 +33523,6 @@ paths: - Fleet service tokens /api/fleet/service-tokens: post: - deprecated: true description: Create a service token operationId: '%2Fapi%2Ffleet%2Fservice-tokens#0' parameters: @@ -36556,6 +36780,456 @@ paths: tags: - Security AI Assistant API - Prompts API + /api/security/role: + get: + operationId: '%2Fapi%2Fsecurity%2Frole#0' + parameters: + - description: The version of the API to use + in: header + name: elastic-api-version + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + - in: query + name: replaceDeprecatedPrivileges + required: false + schema: + type: boolean + responses: {} + summary: Get all roles + tags: + - roles + /api/security/role/{name}: + delete: + operationId: '%2Fapi%2Fsecurity%2Frole%2F%7Bname%7D#1' + parameters: + - description: The version of the API to use + in: header + name: elastic-api-version + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + - description: A required header to protect against CSRF attacks + in: header + name: kbn-xsrf + required: true + schema: + example: 'true' + type: string + - in: path + name: name + required: true + schema: + minLength: 1 + type: string + responses: {} + summary: Delete a role + tags: + - roles + get: + operationId: '%2Fapi%2Fsecurity%2Frole%2F%7Bname%7D#0' + parameters: + - description: The version of the API to use + in: header + name: elastic-api-version + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + - in: path + name: name + required: true + schema: + minLength: 1 + type: string + - in: query + name: replaceDeprecatedPrivileges + required: false + schema: + type: boolean + responses: {} + summary: Get a role + tags: + - roles + put: + operationId: '%2Fapi%2Fsecurity%2Frole%2F%7Bname%7D#2' + parameters: + - description: The version of the API to use + in: header + name: elastic-api-version + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + - description: A required header to protect against CSRF attacks + in: header + name: kbn-xsrf + required: true + schema: + example: 'true' + type: string + - in: path + name: name + required: true + schema: + maxLength: 1024 + minLength: 1 + type: string + - in: query + name: createOnly + required: false + schema: + default: false + type: boolean + requestBody: + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + description: + maxLength: 2048 + type: string + elasticsearch: + additionalProperties: false + type: object + properties: + cluster: + items: + type: string + type: array + indices: + items: + additionalProperties: false + type: object + properties: + allow_restricted_indices: + type: boolean + field_security: + additionalProperties: + items: + type: string + type: array + type: object + names: + items: + type: string + minItems: 1 + type: array + privileges: + items: + type: string + minItems: 1 + type: array + query: + type: string + required: + - names + - privileges + type: array + remote_cluster: + items: + additionalProperties: false + type: object + properties: + clusters: + items: + type: string + minItems: 1 + type: array + privileges: + items: + type: string + minItems: 1 + type: array + required: + - privileges + - clusters + type: array + remote_indices: + items: + additionalProperties: false + type: object + properties: + allow_restricted_indices: + type: boolean + clusters: + items: + type: string + minItems: 1 + type: array + field_security: + additionalProperties: + items: + type: string + type: array + type: object + names: + items: + type: string + minItems: 1 + type: array + privileges: + items: + type: string + minItems: 1 + type: array + query: + type: string + required: + - clusters + - names + - privileges + type: array + run_as: + items: + type: string + type: array + kibana: + items: + additionalProperties: false + type: object + properties: + base: + anyOf: + - items: {} + type: array + - type: boolean + - type: number + - type: object + - type: string + nullable: true + oneOf: + - items: + type: string + type: array + - items: + type: string + type: array + feature: + additionalProperties: + items: + type: string + type: array + type: object + spaces: + anyOf: + - items: + enum: + - '*' + type: string + maxItems: 1 + minItems: 1 + type: array + - items: + type: string + type: array + default: + - '*' + required: + - base + type: array + metadata: + additionalProperties: {} + type: object + required: + - elasticsearch + responses: {} + summary: Create or update a role + tags: + - roles + /api/security/roles: + post: + operationId: '%2Fapi%2Fsecurity%2Froles#0' + parameters: + - description: The version of the API to use + in: header + name: elastic-api-version + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + - description: A required header to protect against CSRF attacks + in: header + name: kbn-xsrf + required: true + schema: + example: 'true' + type: string + requestBody: + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + roles: + additionalProperties: + additionalProperties: false + type: object + properties: + description: + maxLength: 2048 + type: string + elasticsearch: + additionalProperties: false + type: object + properties: + cluster: + items: + type: string + type: array + indices: + items: + additionalProperties: false + type: object + properties: + allow_restricted_indices: + type: boolean + field_security: + additionalProperties: + items: + type: string + type: array + type: object + names: + items: + type: string + minItems: 1 + type: array + privileges: + items: + type: string + minItems: 1 + type: array + query: + type: string + required: + - names + - privileges + type: array + remote_cluster: + items: + additionalProperties: false + type: object + properties: + clusters: + items: + type: string + minItems: 1 + type: array + privileges: + items: + type: string + minItems: 1 + type: array + required: + - privileges + - clusters + type: array + remote_indices: + items: + additionalProperties: false + type: object + properties: + allow_restricted_indices: + type: boolean + clusters: + items: + type: string + minItems: 1 + type: array + field_security: + additionalProperties: + items: + type: string + type: array + type: object + names: + items: + type: string + minItems: 1 + type: array + privileges: + items: + type: string + minItems: 1 + type: array + query: + type: string + required: + - clusters + - names + - privileges + type: array + run_as: + items: + type: string + type: array + kibana: + items: + additionalProperties: false + type: object + properties: + base: + anyOf: + - items: {} + type: array + - type: boolean + - type: number + - type: object + - type: string + nullable: true + oneOf: + - items: + type: string + type: array + - items: + type: string + type: array + feature: + additionalProperties: + items: + type: string + type: array + type: object + spaces: + anyOf: + - items: + enum: + - '*' + type: string + maxItems: 1 + minItems: 1 + type: array + - items: + type: string + type: array + default: + - '*' + required: + - base + type: array + metadata: + additionalProperties: {} + type: object + required: + - elasticsearch + type: object + required: + - roles + responses: {} + summary: Create or update roles + tags: + - roles /api/spaces/space: get: operationId: '%2Fapi%2Fspaces%2Fspace#0' @@ -48211,9 +48885,7 @@ components: name: type: string source: - items: - type: string - type: array + type: string required: - name - source @@ -48365,9 +49037,7 @@ components: name: type: string source: - items: - type: string - type: array + type: string required: - name - source @@ -51504,6 +52174,7 @@ tags: - name: Message Signing Service - description: Machine learning name: ml + - name: roles - description: > Export sets of saved objects that you want to import into {kib}, resolve import errors, and rotate an encryption key for encrypted saved objects diff --git a/oas_docs/output/kibana.staging.yaml b/oas_docs/output/kibana.staging.yaml index 827f453683bca..da06e3748c05b 100644 --- a/oas_docs/output/kibana.staging.yaml +++ b/oas_docs/output/kibana.staging.yaml @@ -13857,12 +13857,21 @@ paths: required: false schema: type: string - - in: query + - description: use withAgentCount instead + in: query name: noAgentCount required: false schema: + deprecated: true type: boolean - - in: query + - description: get policies with agent count + in: query + name: withAgentCount + required: false + schema: + type: boolean + - description: get full policies with package policies populated + in: query name: full required: false schema: @@ -18014,6 +18023,110 @@ paths: summary: '' tags: - Elastic Agent policies + /api/fleet/agent_policies/{agentPolicyId}/outputs: + get: + description: Get list of outputs associated with agent policy by policy id + operationId: '%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Foutputs#0' + parameters: + - description: The version of the API to use + in: header + name: elastic-api-version + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + - in: path + name: agentPolicyId + required: true + schema: + type: string + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + item: + additionalProperties: false + type: object + properties: + agentPolicyId: + type: string + data: + additionalProperties: false + type: object + properties: + integrations: + items: + additionalProperties: false + type: object + properties: + id: + type: string + integrationPolicyName: + type: string + name: + type: string + pkgName: + type: string + type: array + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + monitoring: + additionalProperties: false + type: object + properties: + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + required: + - monitoring + - data + required: + - item + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + description: Generic Error + type: object + properties: + error: + type: string + message: + type: string + statusCode: + type: number + required: + - message + summary: '' + tags: + - Elastic Agent policies /api/fleet/agent_policies/delete: post: description: Delete agent policy by ID @@ -18084,6 +18197,128 @@ paths: summary: '' tags: - Elastic Agent policies + /api/fleet/agent_policies/outputs: + post: + description: Get list of outputs associated with agent policies + operationId: '%2Fapi%2Ffleet%2Fagent_policies%2Foutputs#0' + parameters: + - description: The version of the API to use + in: header + name: elastic-api-version + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + - description: A required header to protect against CSRF attacks + in: header + name: kbn-xsrf + required: true + schema: + example: 'true' + type: string + requestBody: + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + ids: + description: list of package policy ids + items: + type: string + type: array + required: + - ids + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + items: + items: + additionalProperties: false + type: object + properties: + agentPolicyId: + type: string + data: + additionalProperties: false + type: object + properties: + integrations: + items: + additionalProperties: false + type: object + properties: + id: + type: string + integrationPolicyName: + type: string + name: + type: string + pkgName: + type: string + type: array + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + monitoring: + additionalProperties: false + type: object + properties: + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + required: + - monitoring + - data + type: array + required: + - items + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + description: Generic Error + type: object + properties: + error: + type: string + message: + type: string + statusCode: + type: number + required: + - message + summary: '' + tags: + - Elastic Agent policies /api/fleet/agent_status: get: description: Get agent status summary @@ -18260,7 +18495,6 @@ paths: - Elastic Agents /api/fleet/agent-status: get: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fagent-status#0' parameters: - description: The version of the API to use @@ -19963,7 +20197,6 @@ paths: tags: - Elastic Agent actions put: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Freassign#0' parameters: - description: The version of the API to use @@ -21816,7 +22049,6 @@ paths: - Fleet enrollment API keys /api/fleet/enrollment-api-keys: get: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fenrollment-api-keys#0' parameters: - description: The version of the API to use @@ -21848,7 +22080,6 @@ paths: summary: '' tags: [] post: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fenrollment-api-keys#1' parameters: - description: The version of the API to use @@ -21886,7 +22117,6 @@ paths: tags: [] /api/fleet/enrollment-api-keys/{keyId}: delete: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fenrollment-api-keys%2F%7BkeyId%7D#1' parameters: - description: The version of the API to use @@ -21913,7 +22143,6 @@ paths: summary: '' tags: [] get: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fenrollment-api-keys%2F%7BkeyId%7D#0' parameters: - description: The version of the API to use @@ -23628,7 +23857,6 @@ paths: - Elastic Package Manager (EPM) /api/fleet/epm/packages/{pkgkey}: delete: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#3' parameters: - description: The version of the API to use @@ -23667,7 +23895,6 @@ paths: summary: '' tags: [] get: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#0' parameters: - description: The version of the API to use @@ -23708,7 +23935,6 @@ paths: summary: '' tags: [] post: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#2' parameters: - description: The version of the API to use @@ -23764,7 +23990,6 @@ paths: summary: '' tags: [] put: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#1' parameters: - description: The version of the API to use @@ -36727,7 +36952,6 @@ paths: - Fleet service tokens /api/fleet/service-tokens: post: - deprecated: true description: Create a service token operationId: '%2Fapi%2Ffleet%2Fservice-tokens#0' parameters: @@ -57042,9 +57266,7 @@ components: name: type: string source: - items: - type: string - type: array + type: string required: - name - source @@ -57196,9 +57418,7 @@ components: name: type: string source: - items: - type: string - type: array + type: string required: - name - source diff --git a/oas_docs/output/kibana.yaml b/oas_docs/output/kibana.yaml index 827f453683bca..da06e3748c05b 100644 --- a/oas_docs/output/kibana.yaml +++ b/oas_docs/output/kibana.yaml @@ -13857,12 +13857,21 @@ paths: required: false schema: type: string - - in: query + - description: use withAgentCount instead + in: query name: noAgentCount required: false schema: + deprecated: true type: boolean - - in: query + - description: get policies with agent count + in: query + name: withAgentCount + required: false + schema: + type: boolean + - description: get full policies with package policies populated + in: query name: full required: false schema: @@ -18014,6 +18023,110 @@ paths: summary: '' tags: - Elastic Agent policies + /api/fleet/agent_policies/{agentPolicyId}/outputs: + get: + description: Get list of outputs associated with agent policy by policy id + operationId: '%2Fapi%2Ffleet%2Fagent_policies%2F%7BagentPolicyId%7D%2Foutputs#0' + parameters: + - description: The version of the API to use + in: header + name: elastic-api-version + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + - in: path + name: agentPolicyId + required: true + schema: + type: string + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + item: + additionalProperties: false + type: object + properties: + agentPolicyId: + type: string + data: + additionalProperties: false + type: object + properties: + integrations: + items: + additionalProperties: false + type: object + properties: + id: + type: string + integrationPolicyName: + type: string + name: + type: string + pkgName: + type: string + type: array + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + monitoring: + additionalProperties: false + type: object + properties: + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + required: + - monitoring + - data + required: + - item + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + description: Generic Error + type: object + properties: + error: + type: string + message: + type: string + statusCode: + type: number + required: + - message + summary: '' + tags: + - Elastic Agent policies /api/fleet/agent_policies/delete: post: description: Delete agent policy by ID @@ -18084,6 +18197,128 @@ paths: summary: '' tags: - Elastic Agent policies + /api/fleet/agent_policies/outputs: + post: + description: Get list of outputs associated with agent policies + operationId: '%2Fapi%2Ffleet%2Fagent_policies%2Foutputs#0' + parameters: + - description: The version of the API to use + in: header + name: elastic-api-version + schema: + default: '2023-10-31' + enum: + - '2023-10-31' + type: string + - description: A required header to protect against CSRF attacks + in: header + name: kbn-xsrf + required: true + schema: + example: 'true' + type: string + requestBody: + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + ids: + description: list of package policy ids + items: + type: string + type: array + required: + - ids + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + type: object + properties: + items: + items: + additionalProperties: false + type: object + properties: + agentPolicyId: + type: string + data: + additionalProperties: false + type: object + properties: + integrations: + items: + additionalProperties: false + type: object + properties: + id: + type: string + integrationPolicyName: + type: string + name: + type: string + pkgName: + type: string + type: array + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + monitoring: + additionalProperties: false + type: object + properties: + output: + additionalProperties: false + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + required: + - output + required: + - monitoring + - data + type: array + required: + - items + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + additionalProperties: false + description: Generic Error + type: object + properties: + error: + type: string + message: + type: string + statusCode: + type: number + required: + - message + summary: '' + tags: + - Elastic Agent policies /api/fleet/agent_status: get: description: Get agent status summary @@ -18260,7 +18495,6 @@ paths: - Elastic Agents /api/fleet/agent-status: get: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fagent-status#0' parameters: - description: The version of the API to use @@ -19963,7 +20197,6 @@ paths: tags: - Elastic Agent actions put: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fagents%2F%7BagentId%7D%2Freassign#0' parameters: - description: The version of the API to use @@ -21816,7 +22049,6 @@ paths: - Fleet enrollment API keys /api/fleet/enrollment-api-keys: get: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fenrollment-api-keys#0' parameters: - description: The version of the API to use @@ -21848,7 +22080,6 @@ paths: summary: '' tags: [] post: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fenrollment-api-keys#1' parameters: - description: The version of the API to use @@ -21886,7 +22117,6 @@ paths: tags: [] /api/fleet/enrollment-api-keys/{keyId}: delete: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fenrollment-api-keys%2F%7BkeyId%7D#1' parameters: - description: The version of the API to use @@ -21913,7 +22143,6 @@ paths: summary: '' tags: [] get: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fenrollment-api-keys%2F%7BkeyId%7D#0' parameters: - description: The version of the API to use @@ -23628,7 +23857,6 @@ paths: - Elastic Package Manager (EPM) /api/fleet/epm/packages/{pkgkey}: delete: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#3' parameters: - description: The version of the API to use @@ -23667,7 +23895,6 @@ paths: summary: '' tags: [] get: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#0' parameters: - description: The version of the API to use @@ -23708,7 +23935,6 @@ paths: summary: '' tags: [] post: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#2' parameters: - description: The version of the API to use @@ -23764,7 +23990,6 @@ paths: summary: '' tags: [] put: - deprecated: true operationId: '%2Fapi%2Ffleet%2Fepm%2Fpackages%2F%7Bpkgkey%7D#1' parameters: - description: The version of the API to use @@ -36727,7 +36952,6 @@ paths: - Fleet service tokens /api/fleet/service-tokens: post: - deprecated: true description: Create a service token operationId: '%2Fapi%2Ffleet%2Fservice-tokens#0' parameters: @@ -57042,9 +57266,7 @@ components: name: type: string source: - items: - type: string - type: array + type: string required: - name - source @@ -57196,9 +57418,7 @@ components: name: type: string source: - items: - type: string - type: array + type: string required: - name - source diff --git a/package.json b/package.json index 4faf47b17aa72..31ec1fdba494c 100644 --- a/package.json +++ b/package.json @@ -105,7 +105,6 @@ "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/util": "^5.2.0", "@babel/runtime": "^7.24.7", - "@cfworker/json-schema": "^1.12.7", "@dagrejs/dagre": "^1.1.4", "@dnd-kit/core": "^6.1.0", "@dnd-kit/sortable": "^8.0.0", @@ -635,6 +634,7 @@ "@kbn/management-settings-types": "link:packages/kbn-management/settings/types", "@kbn/management-settings-utilities": "link:packages/kbn-management/settings/utilities", "@kbn/management-test-plugin": "link:test/plugin_functional/plugins/management_test_plugin", + "@kbn/manifest": "link:packages/kbn-manifest", "@kbn/mapbox-gl": "link:packages/kbn-mapbox-gl", "@kbn/maps-custom-raster-source-plugin": "link:x-pack/examples/third_party_maps_source_example", "@kbn/maps-ems-plugin": "link:src/plugins/maps_ems", @@ -747,6 +747,7 @@ "@kbn/resizable-layout-examples-plugin": "link:examples/resizable_layout_examples", "@kbn/resolver-test-plugin": "link:x-pack/test/plugin_functional/plugins/resolver_test", "@kbn/response-ops-feature-flag-service": "link:packages/response-ops/feature_flag_service", + "@kbn/response-ops-rule-params": "link:packages/response-ops/rule_params", "@kbn/response-stream-plugin": "link:examples/response_stream", "@kbn/rison": "link:packages/kbn-rison", "@kbn/rollup": "link:x-pack/packages/rollup", @@ -1120,9 +1121,7 @@ "fastest-levenshtein": "^1.0.12", "fflate": "^0.6.9", "file-saver": "^1.3.8", - "flat": "5", "fnv-plus": "^1.3.1", - "font-awesome": "4.7.0", "formik": "^2.4.6", "fp-ts": "^2.3.1", "get-port": "^5.0.0", @@ -1135,7 +1134,6 @@ "he": "^1.2.0", "history": "^4.9.0", "hjson": "3.2.1", - "html2canvas": "^1.4.1", "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.1", "i18n-iso-countries": "^4.3.1", @@ -1333,8 +1331,6 @@ "@elastic/synthetics": "^1.12.1", "@emotion/babel-preset-css-prop": "^11.11.0", "@emotion/jest": "^11.11.0", - "@formatjs/cli": "^6.2.8", - "@formatjs/cli-lib": "^6.3.8", "@frsource/cypress-plugin-visual-regression-diff": "^3.3.10", "@istanbuljs/nyc-config-typescript": "^1.0.2", "@istanbuljs/schema": "^0.1.2", @@ -1501,7 +1497,7 @@ "@octokit/rest": "^17.11.2", "@parcel/watcher": "^2.1.0", "@playwright/test": "=1.46.0", - "@redocly/cli": "^1.25.5", + "@redocly/cli": "^1.25.6", "@statoscope/webpack-plugin": "^5.28.2", "@storybook/addon-a11y": "^6.5.16", "@storybook/addon-actions": "^6.5.16", @@ -1530,9 +1526,6 @@ "@types/archiver": "^5.3.1", "@types/async": "^3.2.3", "@types/aws4": "^1.5.0", - "@types/babel__core": "^7.20.5", - "@types/babel__generator": "^7.6.4", - "@types/babel__helper-plugin-utils": "^7.10.0", "@types/base64-js": "^1.2.5", "@types/byte-size": "^8.1.0", "@types/chance": "^1.0.0", @@ -1558,13 +1551,11 @@ "@types/ejs": "^3.0.6", "@types/enzyme": "^3.10.12", "@types/eslint": "^8.44.2", - "@types/event-stream": "^4.0.5", "@types/express": "^4.17.21", "@types/extract-zip": "^1.6.2", "@types/faker": "^5.1.5", "@types/fetch-mock": "^7.3.1", "@types/file-saver": "^2.0.0", - "@types/flat": "^5.0.5", "@types/flot": "^0.0.31", "@types/fnv-plus": "^1.3.0", "@types/geojson": "^7946.0.10", @@ -1593,7 +1584,6 @@ "@types/lz-string": "^1.3.34", "@types/mapbox__vector-tile": "1.3.0", "@types/markdown-it": "^12.2.3", - "@types/md5": "^2.2.0", "@types/micromatch": "^4.0.2", "@types/mime": "^2.0.1", "@types/mime-types": "^2.1.0", @@ -1616,7 +1606,6 @@ "@types/papaparse": "^5.0.3", "@types/pbf": "3.0.2", "@types/pdfmake": "^0.2.2", - "@types/pegjs": "^0.10.1", "@types/picomatch": "^2.3.0", "@types/pidusage": "^2.0.2", "@types/pixelmatch": "^5.2.4", @@ -1648,7 +1637,6 @@ "@types/stats-lite": "^2.2.0", "@types/styled-components": "^5.1.0", "@types/supertest": "^6.0.2", - "@types/tapable": "^1.0.6", "@types/textarea-caret": "^3.0.1", "@types/tinycolor2": "^1.4.1", "@types/tough-cookie": "^4.0.5", @@ -1666,7 +1654,6 @@ "@types/yargs": "^15.0.0", "@types/yauzl": "^2.9.1", "@types/yazl": "^2.4.2", - "@types/zen-observable": "^0.8.0", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", "@typescript-eslint/typescript-estree": "^5.62.0", @@ -1707,7 +1694,6 @@ "cypress-real-events": "^1.11.0", "cypress-recurse": "^1.35.2", "date-fns": "^2.29.3", - "debug": "^2.6.9", "dependency-check": "^4.1.0", "ejs": "^3.1.10", "enzyme": "^3.11.0", @@ -1744,7 +1730,6 @@ "gulp-postcss": "^9.0.1", "gulp-terser": "^2.1.0", "has-ansi": "^3.0.0", - "html": "1.0.0", "html-loader": "^1.3.2", "http-proxy": "^1.18.1", "http2-proxy": "^5.0.53", @@ -1781,7 +1766,7 @@ "mochawesome-merge": "^4.3.0", "mock-fs": "^5.1.2", "ms-chromium-edge-driver": "^0.5.1", - "msw": "^2.4.9", + "msw": "^2.4.11", "multistream": "^4.1.0", "mutation-observer": "^1.0.3", "native-hdr-histogram": "^1.0.0", @@ -1791,7 +1776,6 @@ "oboe": "^2.1.4", "openapi-types": "^10.0.0", "p-reflect": "2.1.0", - "pbf": "3.2.1", "peggy": "^1.2.0", "picomatch": "^2.3.1", "pidusage": "^3.0.2", diff --git a/packages/core/apps/core-apps-server-internal/src/core_app.ts b/packages/core/apps/core-apps-server-internal/src/core_app.ts index 10552864fcc7e..51b94e90df570 100644 --- a/packages/core/apps/core-apps-server-internal/src/core_app.ts +++ b/packages/core/apps/core-apps-server-internal/src/core_app.ts @@ -248,6 +248,7 @@ export class CoreAppsService { if (latestOverrideVersion !== persistedOverrides.version) { this.configService.setDynamicConfigOverrides(persistedOverrides.attributes); latestOverrideVersion = persistedOverrides.version; + this.logger.info('Succeeded in applying persisted dynamic config overrides'); } } catch (err) { // Potential failures: diff --git a/packages/core/deprecations/core-deprecations-browser-internal/src/deprecations_client.test.ts b/packages/core/deprecations/core-deprecations-browser-internal/src/deprecations_client.test.ts index bfb4f35fa93f1..38777474f136b 100644 --- a/packages/core/deprecations/core-deprecations-browser-internal/src/deprecations_client.test.ts +++ b/packages/core/deprecations/core-deprecations-browser-internal/src/deprecations_client.test.ts @@ -135,7 +135,7 @@ describe('DeprecationsClient', () => { expect(result).toMatchInlineSnapshot(` Object { - "reason": "This deprecation cannot be resolved automatically.", + "reason": "This deprecation cannot be resolved automatically or marked as resolved.", "status": "fail", } `); diff --git a/packages/core/deprecations/core-deprecations-browser-internal/src/deprecations_client.ts b/packages/core/deprecations/core-deprecations-browser-internal/src/deprecations_client.ts index f1f473d726b7e..bdcad406995a9 100644 --- a/packages/core/deprecations/core-deprecations-browser-internal/src/deprecations_client.ts +++ b/packages/core/deprecations/core-deprecations-browser-internal/src/deprecations_client.ts @@ -8,7 +8,7 @@ */ import { i18n } from '@kbn/i18n'; -import type { HttpStart } from '@kbn/core-http-browser'; +import type { HttpFetchOptionsWithPath, HttpStart } from '@kbn/core-http-browser'; import type { DomainDeprecationDetails, DeprecationsGetResponse, @@ -47,23 +47,15 @@ export class DeprecationsClient { return typeof details.correctiveActions.api === 'object'; }; - public resolveDeprecation = async ( + private getResolveFetchDetails = ( details: DomainDeprecationDetails - ): Promise => { + ): HttpFetchOptionsWithPath | undefined => { const { domainId, correctiveActions } = details; - // explicit check required for TS type guard - if (typeof correctiveActions.api !== 'object') { - return { - status: 'fail', - reason: i18n.translate('core.deprecations.noCorrectiveAction', { - defaultMessage: 'This deprecation cannot be resolved automatically.', - }), - }; - } - const { body, method, path, omitContextFromBody = false } = correctiveActions.api; - try { - await this.http.fetch({ + if (correctiveActions.api) { + const { body, method, path, omitContextFromBody = false } = correctiveActions.api; + + return { path, method, asSystemRequest: true, @@ -71,7 +63,54 @@ export class DeprecationsClient { ...body, ...(omitContextFromBody ? {} : { deprecationDetails: { domainId } }), }), - }); + }; + } + + if (correctiveActions.mark_as_resolved_api) { + const { routeMethod, routePath, routeVersion, apiTotalCalls, totalMarkedAsResolved } = + correctiveActions.mark_as_resolved_api; + const incrementBy = apiTotalCalls - totalMarkedAsResolved; + + return { + path: '/api/deprecations/mark_as_resolved', + method: 'POST', + asSystemRequest: true, + body: JSON.stringify({ + domainId, + routeMethod, + routePath, + routeVersion, + incrementBy, + }), + }; + } + }; + + public resolveDeprecation = async ( + details: DomainDeprecationDetails + ): Promise => { + const { correctiveActions } = details; + const noCorrectiveActionFail = { + status: 'fail' as const, + reason: i18n.translate('core.deprecations.noCorrectiveAction', { + defaultMessage: 'This deprecation cannot be resolved automatically or marked as resolved.', + }), + }; + + if ( + typeof correctiveActions.api !== 'object' && + typeof correctiveActions.mark_as_resolved_api !== 'object' + ) { + return noCorrectiveActionFail; + } + + try { + const fetchParams = this.getResolveFetchDetails(details); + if (!fetchParams) { + return noCorrectiveActionFail; + } + + await this.http.fetch(fetchParams); return { status: 'ok' }; } catch (err) { return { diff --git a/packages/core/deprecations/core-deprecations-common/src/types.ts b/packages/core/deprecations/core-deprecations-common/src/types.ts index bf9a4a673d721..85da30b2c1287 100644 --- a/packages/core/deprecations/core-deprecations-common/src/types.ts +++ b/packages/core/deprecations/core-deprecations-common/src/types.ts @@ -22,7 +22,7 @@ export interface BaseDeprecationDetails { * The description message to be displayed for the deprecation. * Check the README for writing deprecations in `src/core/server/deprecations/README.mdx` */ - message: string; + message: string | string[]; /** * levels: * - warning: will not break deployment upon upgrade @@ -39,7 +39,7 @@ export interface BaseDeprecationDetails { * Predefined types are necessary to reduce having similar definitions with different keywords * across kibana deprecations. */ - deprecationType?: 'config' | 'feature'; + deprecationType?: 'config' | 'api' | 'feature'; /** (optional) link to the documentation for more details on the deprecation. */ documentationUrl?: string; /** (optional) specify the fix for this deprecation requires a full kibana restart. */ @@ -70,9 +70,31 @@ export interface BaseDeprecationDetails { * Check the README for writing deprecations in `src/core/server/deprecations/README.mdx` */ manualSteps: string[]; + /** + * (optional) The api to be called to mark the deprecation as resolved + * This corrective action when called should not resolve the deprecation + * instead it helps users track manually deprecated apis + * If the API used does resolve the deprecation use `correctiveActions.api` + */ + mark_as_resolved_api?: { + apiTotalCalls: number; + totalMarkedAsResolved: number; + timestamp: Date | number | string; + routePath: string; + routeMethod: string; + routeVersion?: string; + }; }; } +/** + * @public + */ +export interface ApiDeprecationDetails extends BaseDeprecationDetails { + apiId: string; + deprecationType: 'api'; +} + /** * @public */ @@ -91,7 +113,10 @@ export interface FeatureDeprecationDetails extends BaseDeprecationDetails { /** * @public */ -export type DeprecationsDetails = ConfigDeprecationDetails | FeatureDeprecationDetails; +export type DeprecationsDetails = + | ConfigDeprecationDetails + | ApiDeprecationDetails + | FeatureDeprecationDetails; /** * @public diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations.test.ts b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations.test.ts new file mode 100644 index 0000000000000..b431088152f3e --- /dev/null +++ b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations.test.ts @@ -0,0 +1,353 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import type { DeepPartial } from '@kbn/utility-types'; +import { mockDeprecationsRegistry, mockDeprecationsFactory } from '../mocks'; +import { + registerApiDeprecationsInfo, + buildApiDeprecationId, + createGetApiDeprecations, +} from './api_deprecations'; +import { RouterDeprecatedRouteDetails } from '@kbn/core-http-server'; +import { httpServiceMock } from '@kbn/core-http-server-mocks'; +import { + coreUsageDataServiceMock, + coreUsageStatsClientMock, +} from '@kbn/core-usage-data-server-mocks'; +import _ from 'lodash'; +import { CoreDeprecatedApiUsageStats } from '@kbn/core-usage-data-server'; + +describe('#registerApiDeprecationsInfo', () => { + const deprecationsFactory = mockDeprecationsFactory.create(); + const deprecationsRegistry = mockDeprecationsRegistry.create(); + let usageClientMock: ReturnType; + let http: ReturnType; + let coreUsageData: ReturnType; + + beforeEach(() => { + jest.clearAllMocks(); + usageClientMock = coreUsageStatsClientMock.create(); + http = httpServiceMock.createInternalSetupContract(); + coreUsageData = coreUsageDataServiceMock.createSetupContract(usageClientMock); + }); + + beforeAll(() => { + jest.useFakeTimers().setSystemTime(new Date('2024-10-17T12:06:41.224Z')); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + it('registers api deprecations', async () => { + deprecationsFactory.getRegistry.mockReturnValue(deprecationsRegistry); + registerApiDeprecationsInfo({ deprecationsFactory, coreUsageData, http }); + + expect(deprecationsFactory.getRegistry).toBeCalledWith('core.api_deprecations'); + expect(deprecationsRegistry.registerDeprecations).toBeCalledTimes(1); + expect(deprecationsRegistry.registerDeprecations).toBeCalledWith({ + getDeprecations: expect.any(Function), + }); + }); + + describe('#createGetApiDeprecations', () => { + const createDeprecatedRouteDetails = ( + overrides?: DeepPartial + ): RouterDeprecatedRouteDetails => + _.merge( + { + routeDeprecationOptions: { + documentationUrl: 'https://fake-url', + severity: 'critical', + reason: { + type: 'remove', + }, + }, + routeMethod: 'get', + routePath: '/api/test/', + routeVersion: '123', + } as RouterDeprecatedRouteDetails, + overrides + ); + + const createApiUsageStat = ( + apiId: string, + overrides?: DeepPartial + ): CoreDeprecatedApiUsageStats => + _.merge( + { + apiId, + totalMarkedAsResolved: 1, + markedAsResolvedLastCalledAt: '2024-10-17T12:06:41.224Z', + apiTotalCalls: 13, + apiLastCalledAt: '2024-09-01T10:06:41.224Z', + }, + overrides + ); + + it('returns removed type deprecated route', async () => { + const getDeprecations = createGetApiDeprecations({ coreUsageData, http }); + const deprecatedRoute = createDeprecatedRouteDetails({ + routePath: '/api/test_removed/', + routeDeprecationOptions: { reason: { type: 'remove' } }, + }); + http.getRegisteredDeprecatedApis.mockReturnValue([deprecatedRoute]); + usageClientMock.getDeprecatedApiUsageStats.mockResolvedValue([ + createApiUsageStat(buildApiDeprecationId(deprecatedRoute)), + ]); + + const deprecations = await getDeprecations(); + expect(deprecations).toMatchInlineSnapshot(` + Array [ + Object { + "apiId": "123|get|/api/test_removed", + "correctiveActions": Object { + "manualSteps": Array [ + "Identify the origin of these API calls.", + "This API no longer exists and no replacement is available. Delete any requests you have that use this API.", + "Check that you are no longer using the old API in any requests, and mark this issue as resolved. It will no longer appear in the Upgrade Assistant unless another call using this API is detected.", + ], + "mark_as_resolved_api": Object { + "apiTotalCalls": 13, + "routeMethod": "get", + "routePath": "/api/test_removed/", + "routeVersion": "123", + "timestamp": 2024-10-17T12:06:41.224Z, + "totalMarkedAsResolved": 1, + }, + }, + "deprecationType": "api", + "documentationUrl": "https://fake-url", + "domainId": "core.routes-deprecations", + "level": "critical", + "message": Array [ + "The API \\"GET /api/test_removed/\\" has been called 13 times. The last call was on Sunday, September 1, 2024 6:06 AM -04:00.", + "This issue has been marked as resolved on Thursday, October 17, 2024 8:06 AM -04:00 but the API has been called 12 times since.", + ], + "title": "The \\"GET /api/test_removed/\\" route is removed", + }, + ] + `); + }); + + it('returns migrated type deprecated route', async () => { + const getDeprecations = createGetApiDeprecations({ coreUsageData, http }); + const deprecatedRoute = createDeprecatedRouteDetails({ + routePath: '/api/test_migrated/', + routeDeprecationOptions: { + reason: { type: 'migrate', newApiMethod: 'post', newApiPath: '/api/new_path' }, + }, + }); + http.getRegisteredDeprecatedApis.mockReturnValue([deprecatedRoute]); + usageClientMock.getDeprecatedApiUsageStats.mockResolvedValue([ + createApiUsageStat(buildApiDeprecationId(deprecatedRoute)), + ]); + + const deprecations = await getDeprecations(); + expect(deprecations).toMatchInlineSnapshot(` + Array [ + Object { + "apiId": "123|get|/api/test_migrated", + "correctiveActions": Object { + "manualSteps": Array [ + "Identify the origin of these API calls.", + "Update the requests to use the following new API instead: \\"POST /api/new_path\\".", + "Check that you are no longer using the old API in any requests, and mark this issue as resolved. It will no longer appear in the Upgrade Assistant unless another call using this API is detected.", + ], + "mark_as_resolved_api": Object { + "apiTotalCalls": 13, + "routeMethod": "get", + "routePath": "/api/test_migrated/", + "routeVersion": "123", + "timestamp": 2024-10-17T12:06:41.224Z, + "totalMarkedAsResolved": 1, + }, + }, + "deprecationType": "api", + "documentationUrl": "https://fake-url", + "domainId": "core.routes-deprecations", + "level": "critical", + "message": Array [ + "The API \\"GET /api/test_migrated/\\" has been called 13 times. The last call was on Sunday, September 1, 2024 6:06 AM -04:00.", + "This issue has been marked as resolved on Thursday, October 17, 2024 8:06 AM -04:00 but the API has been called 12 times since.", + ], + "title": "The \\"GET /api/test_migrated/\\" route is migrated to a different API", + }, + ] + `); + }); + + it('returns bumped type deprecated route', async () => { + const getDeprecations = createGetApiDeprecations({ coreUsageData, http }); + const deprecatedRoute = createDeprecatedRouteDetails({ + routePath: '/api/test_bumped/', + routeDeprecationOptions: { reason: { type: 'bump', newApiVersion: '444' } }, + }); + http.getRegisteredDeprecatedApis.mockReturnValue([deprecatedRoute]); + usageClientMock.getDeprecatedApiUsageStats.mockResolvedValue([ + createApiUsageStat(buildApiDeprecationId(deprecatedRoute)), + ]); + + const deprecations = await getDeprecations(); + expect(deprecations).toMatchInlineSnapshot(` + Array [ + Object { + "apiId": "123|get|/api/test_bumped", + "correctiveActions": Object { + "manualSteps": Array [ + "Identify the origin of these API calls.", + "Update the requests to use the following new version of the API instead: \\"444\\".", + "Check that you are no longer using the old API in any requests, and mark this issue as resolved. It will no longer appear in the Upgrade Assistant unless another call using this API is detected.", + ], + "mark_as_resolved_api": Object { + "apiTotalCalls": 13, + "routeMethod": "get", + "routePath": "/api/test_bumped/", + "routeVersion": "123", + "timestamp": 2024-10-17T12:06:41.224Z, + "totalMarkedAsResolved": 1, + }, + }, + "deprecationType": "api", + "documentationUrl": "https://fake-url", + "domainId": "core.routes-deprecations", + "level": "critical", + "message": Array [ + "The API \\"GET /api/test_bumped/\\" has been called 13 times. The last call was on Sunday, September 1, 2024 6:06 AM -04:00.", + "This issue has been marked as resolved on Thursday, October 17, 2024 8:06 AM -04:00 but the API has been called 12 times since.", + ], + "title": "The \\"GET /api/test_bumped/\\" route has a newer version available", + }, + ] + `); + }); + + it('does not return resolved deprecated route', async () => { + const getDeprecations = createGetApiDeprecations({ coreUsageData, http }); + const deprecatedRoute = createDeprecatedRouteDetails({ routePath: '/api/test_resolved/' }); + http.getRegisteredDeprecatedApis.mockReturnValue([deprecatedRoute]); + usageClientMock.getDeprecatedApiUsageStats.mockResolvedValue([ + createApiUsageStat(buildApiDeprecationId(deprecatedRoute), { + apiTotalCalls: 5, + totalMarkedAsResolved: 5, + }), + ]); + + const deprecations = await getDeprecations(); + expect(deprecations).toEqual([]); + }); + + it('returns never resolved deprecated route', async () => { + const getDeprecations = createGetApiDeprecations({ coreUsageData, http }); + const deprecatedRoute = createDeprecatedRouteDetails({ + routePath: '/api/test_never_resolved/', + }); + http.getRegisteredDeprecatedApis.mockReturnValue([deprecatedRoute]); + usageClientMock.getDeprecatedApiUsageStats.mockResolvedValue([ + createApiUsageStat(buildApiDeprecationId(deprecatedRoute), { + totalMarkedAsResolved: 0, + markedAsResolvedLastCalledAt: undefined, + }), + ]); + + const deprecations = await getDeprecations(); + expect(deprecations).toMatchInlineSnapshot(` + Array [ + Object { + "apiId": "123|get|/api/test_never_resolved", + "correctiveActions": Object { + "manualSteps": Array [ + "Identify the origin of these API calls.", + "This API no longer exists and no replacement is available. Delete any requests you have that use this API.", + "Check that you are no longer using the old API in any requests, and mark this issue as resolved. It will no longer appear in the Upgrade Assistant unless another call using this API is detected.", + ], + "mark_as_resolved_api": Object { + "apiTotalCalls": 13, + "routeMethod": "get", + "routePath": "/api/test_never_resolved/", + "routeVersion": "123", + "timestamp": 2024-10-17T12:06:41.224Z, + "totalMarkedAsResolved": 0, + }, + }, + "deprecationType": "api", + "documentationUrl": "https://fake-url", + "domainId": "core.routes-deprecations", + "level": "critical", + "message": Array [ + "The API \\"GET /api/test_never_resolved/\\" has been called 13 times. The last call was on Sunday, September 1, 2024 6:06 AM -04:00.", + ], + "title": "The \\"GET /api/test_never_resolved/\\" route is removed", + }, + ] + `); + }); + + it('does not return deprecated routes that have never been called', async () => { + const getDeprecations = createGetApiDeprecations({ coreUsageData, http }); + const deprecatedRoute = createDeprecatedRouteDetails({ + routePath: '/api/test_never_resolved/', + }); + http.getRegisteredDeprecatedApis.mockReturnValue([deprecatedRoute]); + usageClientMock.getDeprecatedApiUsageStats.mockResolvedValue([]); + expect(await getDeprecations()).toEqual([]); + + usageClientMock.getDeprecatedApiUsageStats.mockResolvedValue([ + createApiUsageStat(buildApiDeprecationId(deprecatedRoute), { + apiTotalCalls: 0, + apiLastCalledAt: undefined, + totalMarkedAsResolved: 0, + markedAsResolvedLastCalledAt: undefined, + }), + ]); + expect(await getDeprecations()).toEqual([]); + }); + }); +}); + +describe('#buildApiDeprecationId', () => { + it('returns apiDeprecationId string for versioned routes', () => { + const apiDeprecationId = buildApiDeprecationId({ + routeMethod: 'get', + routePath: '/api/test', + routeVersion: '10-10-2023', + }); + expect(apiDeprecationId).toBe('10-10-2023|get|/api/test'); + }); + + it('returns apiDeprecationId string for unversioned routes', () => { + const apiDeprecationId = buildApiDeprecationId({ + routeMethod: 'get', + routePath: '/api/test', + }); + expect(apiDeprecationId).toBe('unversioned|get|/api/test'); + }); + + it('gives the same ID the route method is capitalized or not', () => { + const apiDeprecationId = buildApiDeprecationId({ + // @ts-expect-error + routeMethod: 'GeT', + routePath: '/api/test', + routeVersion: '10-10-2023', + }); + + expect(apiDeprecationId).toBe('10-10-2023|get|/api/test'); + }); + + it('gives the same ID the route path has a trailing slash or not', () => { + const apiDeprecationId = buildApiDeprecationId({ + // @ts-expect-error + routeMethod: 'GeT', + routePath: '/api/test/', + routeVersion: '10-10-2023', + }); + + expect(apiDeprecationId).toBe('10-10-2023|get|/api/test'); + }); +}); diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations.ts b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations.ts new file mode 100644 index 0000000000000..45893987ddf92 --- /dev/null +++ b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import type { InternalHttpServiceSetup } from '@kbn/core-http-server-internal'; +import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal'; +import type { RouterDeprecatedRouteDetails } from '@kbn/core-http-server'; +import { DeprecationsDetails } from '@kbn/core-deprecations-common'; +import type { DeprecationsFactory } from '../deprecations_factory'; +import { + getApiDeprecationMessage, + getApiDeprecationsManualSteps, + getApiDeprecationTitle, +} from './i18n_texts'; + +interface ApiDeprecationsServiceDeps { + deprecationsFactory: DeprecationsFactory; + http: InternalHttpServiceSetup; + coreUsageData: InternalCoreUsageDataSetup; +} + +export const buildApiDeprecationId = ({ + routePath, + routeMethod, + routeVersion, +}: Pick): string => { + return [ + routeVersion || 'unversioned', + routeMethod.toLocaleLowerCase(), + routePath.replace(/\/$/, ''), + ].join('|'); +}; + +export const createGetApiDeprecations = + ({ http, coreUsageData }: Pick) => + async (): Promise => { + const deprecatedRoutes = http.getRegisteredDeprecatedApis(); + const usageClient = coreUsageData.getClient(); + const deprecatedApiUsageStats = await usageClient.getDeprecatedApiUsageStats(); + + return deprecatedApiUsageStats + .filter(({ apiTotalCalls, totalMarkedAsResolved }) => { + return apiTotalCalls > totalMarkedAsResolved; + }) + .filter(({ apiId }) => + deprecatedRoutes.some((routeDetails) => buildApiDeprecationId(routeDetails) === apiId) + ) + .map((apiUsageStats) => { + const { apiId, apiTotalCalls, totalMarkedAsResolved } = apiUsageStats; + const routeDeprecationDetails = deprecatedRoutes.find( + (routeDetails) => buildApiDeprecationId(routeDetails) === apiId + )!; + const { routeVersion, routePath, routeDeprecationOptions, routeMethod } = + routeDeprecationDetails; + + const deprecationLevel = routeDeprecationOptions.severity || 'warning'; + + return { + apiId, + title: getApiDeprecationTitle(routeDeprecationDetails), + level: deprecationLevel, + message: getApiDeprecationMessage(routeDeprecationDetails, apiUsageStats), + documentationUrl: routeDeprecationOptions.documentationUrl, + correctiveActions: { + manualSteps: getApiDeprecationsManualSteps(routeDeprecationDetails), + mark_as_resolved_api: { + routePath, + routeMethod, + routeVersion, + apiTotalCalls, + totalMarkedAsResolved, + timestamp: new Date(), + }, + }, + deprecationType: 'api', + domainId: 'core.routes-deprecations', + }; + }); + }; + +export const registerApiDeprecationsInfo = ({ + deprecationsFactory, + http, + coreUsageData, +}: ApiDeprecationsServiceDeps): void => { + const deprecationsRegistery = deprecationsFactory.getRegistry('core.api_deprecations'); + + deprecationsRegistery.registerDeprecations({ + getDeprecations: createGetApiDeprecations({ http, coreUsageData }), + }); +}; diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/config_deprecations.test.ts b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/config_deprecations.test.ts new file mode 100644 index 0000000000000..92d0703c8037e --- /dev/null +++ b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/config_deprecations.test.ts @@ -0,0 +1,115 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { registerConfigDeprecationsInfo } from './config_deprecations'; +import { mockDeprecationsRegistry, mockDeprecationsFactory } from '../mocks'; +import { mockCoreContext } from '@kbn/core-base-server-mocks'; +import { configServiceMock } from '@kbn/config-mocks'; + +describe('#registerConfigDeprecationsInfo', () => { + let coreContext: ReturnType; + + const deprecationsFactory = mockDeprecationsFactory.create(); + const deprecationsRegistry = mockDeprecationsRegistry.create(); + const getDeprecationsContext = mockDeprecationsRegistry.createGetDeprecationsContext(); + + beforeEach(() => { + const configService = configServiceMock.create({ + atPath: { skip_deprecated_settings: ['hello', 'world'] }, + }); + jest.clearAllMocks(); + coreContext = mockCoreContext.create({ configService }); + }); + + it('registers config deprecations', async () => { + coreContext.configService.getHandledDeprecatedConfigs.mockReturnValue([ + [ + 'testDomain', + [ + { + configPath: 'test', + level: 'critical', + message: 'testMessage', + documentationUrl: 'testDocUrl', + correctiveActions: { + manualSteps: [ + 'Using Kibana user management, change all users using the kibana_user role to the kibana_admin role.', + 'Using Kibana role-mapping management, change all role-mappings which assign the kibana_user role to the kibana_admin role.', + ], + }, + }, + ], + ], + ]); + + deprecationsFactory.getRegistry.mockReturnValue(deprecationsRegistry); + registerConfigDeprecationsInfo({ + deprecationsFactory, + configService: coreContext.configService, + }); + + expect(coreContext.configService.getHandledDeprecatedConfigs).toBeCalledTimes(1); + expect(deprecationsFactory.getRegistry).toBeCalledTimes(1); + expect(deprecationsFactory.getRegistry).toBeCalledWith('testDomain'); + expect(deprecationsRegistry.registerDeprecations).toBeCalledTimes(1); + const configDeprecations = + await deprecationsRegistry.registerDeprecations.mock.calls[0][0].getDeprecations( + getDeprecationsContext + ); + expect(configDeprecations).toMatchInlineSnapshot(` + Array [ + Object { + "configPath": "test", + "correctiveActions": Object { + "manualSteps": Array [ + "Using Kibana user management, change all users using the kibana_user role to the kibana_admin role.", + "Using Kibana role-mapping management, change all role-mappings which assign the kibana_user role to the kibana_admin role.", + ], + }, + "deprecationType": "config", + "documentationUrl": "testDocUrl", + "level": "critical", + "message": "testMessage", + "requireRestart": true, + "title": "testDomain has a deprecated setting", + }, + ] + `); + }); + + it('accepts `level` field overrides', async () => { + coreContext.configService.getHandledDeprecatedConfigs.mockReturnValue([ + [ + 'testDomain', + [ + { + configPath: 'test', + message: 'testMessage', + level: 'warning', + correctiveActions: { + manualSteps: ['step a'], + }, + }, + ], + ], + ]); + + deprecationsFactory.getRegistry.mockReturnValue(deprecationsRegistry); + registerConfigDeprecationsInfo({ + deprecationsFactory, + configService: coreContext.configService, + }); + + const configDeprecations = + await deprecationsRegistry.registerDeprecations.mock.calls[0][0].getDeprecations( + getDeprecationsContext + ); + expect(configDeprecations[0].level).toBe('warning'); + }); +}); diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/config_deprecations.ts b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/config_deprecations.ts new file mode 100644 index 0000000000000..f9df0edacd9d1 --- /dev/null +++ b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/config_deprecations.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import type { IConfigService } from '@kbn/config'; +import { DeprecationsFactory } from '../deprecations_factory'; + +interface RegisterConfigDeprecationsInfo { + deprecationsFactory: DeprecationsFactory; + configService: IConfigService; +} + +export const registerConfigDeprecationsInfo = ({ + deprecationsFactory, + configService, +}: RegisterConfigDeprecationsInfo) => { + const handledDeprecatedConfigs = configService.getHandledDeprecatedConfigs(); + + for (const [domainId, deprecationsContexts] of handledDeprecatedConfigs) { + const deprecationsRegistry = deprecationsFactory.getRegistry(domainId); + deprecationsRegistry.registerDeprecations({ + getDeprecations: () => { + return deprecationsContexts.map( + ({ + configPath, + title = `${domainId} has a deprecated setting`, + level, + message, + correctiveActions, + documentationUrl, + }) => ({ + configPath, + title, + level, + message, + correctiveActions, + documentationUrl, + deprecationType: 'config', + requireRestart: true, + }) + ); + }, + }); + } +}; diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/i18n_texts.ts b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/i18n_texts.ts new file mode 100644 index 0000000000000..cb1dacc97bd91 --- /dev/null +++ b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/i18n_texts.ts @@ -0,0 +1,132 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { RouterDeprecatedRouteDetails } from '@kbn/core-http-server'; +import { CoreDeprecatedApiUsageStats } from '@kbn/core-usage-data-server'; +import { i18n } from '@kbn/i18n'; +import moment from 'moment'; + +export const getApiDeprecationTitle = (details: RouterDeprecatedRouteDetails) => { + const { routePath, routeMethod, routeDeprecationOptions } = details; + const deprecationType = routeDeprecationOptions.reason.type; + const routeWithMethod = `${routeMethod.toUpperCase()} ${routePath}`; + const deprecationTypeText = i18n.translate('core.deprecations.deprecations.apiDeprecationType', { + defaultMessage: + '{deprecationType, select, remove {is removed} bump {has a newer version available} migrate {is migrated to a different API} other {is deprecated}}', + values: { deprecationType }, + }); + + return i18n.translate('core.deprecations.deprecations.apiDeprecationInfoTitle', { + defaultMessage: 'The "{routeWithMethod}" route {deprecationTypeText}', + values: { + routeWithMethod, + deprecationTypeText, + }, + }); +}; + +export const getApiDeprecationMessage = ( + details: RouterDeprecatedRouteDetails, + apiUsageStats: CoreDeprecatedApiUsageStats +): string[] => { + const { routePath, routeMethod } = details; + const { apiLastCalledAt, apiTotalCalls, markedAsResolvedLastCalledAt, totalMarkedAsResolved } = + apiUsageStats; + + const diff = apiTotalCalls - totalMarkedAsResolved; + const wasResolvedBefore = totalMarkedAsResolved > 0; + const routeWithMethod = `${routeMethod.toUpperCase()} ${routePath}`; + + const messages = [ + i18n.translate('core.deprecations.deprecations.apiDeprecationApiCallsDetailsMessage', { + defaultMessage: + 'The API "{routeWithMethod}" has been called {apiTotalCalls} times. The last call was on {apiLastCalledAt}.', + values: { + routeWithMethod, + apiTotalCalls, + apiLastCalledAt: moment(apiLastCalledAt).format('LLLL Z'), + }, + }), + ]; + + if (wasResolvedBefore) { + messages.push( + i18n.translate( + 'core.deprecations.deprecations.apiDeprecationPreviouslyMarkedAsResolvedMessage', + { + defaultMessage: + 'This issue has been marked as resolved on {markedAsResolvedLastCalledAt} but the API has been called {timeSinceLastResolved, plural, one {# time} other {# times}} since.', + values: { + timeSinceLastResolved: diff, + markedAsResolvedLastCalledAt: moment(markedAsResolvedLastCalledAt).format('LLLL Z'), + }, + } + ) + ); + } + + return messages; +}; + +export const getApiDeprecationsManualSteps = (details: RouterDeprecatedRouteDetails): string[] => { + const { routeDeprecationOptions } = details; + const deprecationType = routeDeprecationOptions.reason.type; + + const manualSteps = [ + i18n.translate('core.deprecations.deprecations.manualSteps.apiIseprecatedStep', { + defaultMessage: 'Identify the origin of these API calls.', + }), + ]; + + switch (deprecationType) { + case 'bump': { + const { newApiVersion } = routeDeprecationOptions.reason; + manualSteps.push( + i18n.translate('core.deprecations.deprecations.manualSteps.bumpDetailsStep', { + defaultMessage: + 'Update the requests to use the following new version of the API instead: "{newApiVersion}".', + values: { newApiVersion }, + }) + ); + break; + } + + case 'remove': { + manualSteps.push( + i18n.translate('core.deprecations.deprecations.manualSteps.removeTypeExplainationStep', { + defaultMessage: + 'This API no longer exists and no replacement is available. Delete any requests you have that use this API.', + }) + ); + break; + } + case 'migrate': { + const { newApiPath, newApiMethod } = routeDeprecationOptions.reason; + const newRouteWithMethod = `${newApiMethod.toUpperCase()} ${newApiPath}`; + + manualSteps.push( + i18n.translate('core.deprecations.deprecations.manualSteps.migrateDetailsStep', { + defaultMessage: + 'Update the requests to use the following new API instead: "{newRouteWithMethod}".', + values: { newRouteWithMethod }, + }) + ); + break; + } + } + + manualSteps.push( + i18n.translate('core.deprecations.deprecations.manualSteps.markAsResolvedStep', { + defaultMessage: + 'Check that you are no longer using the old API in any requests, and mark this issue as resolved. It will no longer appear in the Upgrade Assistant unless another call using this API is detected.', + }) + ); + + return manualSteps; +}; diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/index.ts b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/index.ts new file mode 100644 index 0000000000000..aecf3d5b299a2 --- /dev/null +++ b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +export { buildApiDeprecationId, registerApiDeprecationsInfo } from './api_deprecations'; +export { registerConfigDeprecationsInfo } from './config_deprecations'; diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_service.test.mocks.ts b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_service.test.mocks.ts index 9ce9f52fb7a50..93550539343e3 100644 --- a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_service.test.mocks.ts +++ b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_service.test.mocks.ts @@ -14,6 +14,14 @@ export const DeprecationsFactoryMock = jest .fn() .mockImplementation(() => mockedDeprecationFactoryInstance); +export const registerConfigDeprecationsInfoMock = jest.fn(); +export const registerApiDeprecationsInfoMock = jest.fn(); + +jest.doMock('./deprecations', () => ({ + registerConfigDeprecationsInfo: registerConfigDeprecationsInfoMock, + registerApiDeprecationsInfo: registerApiDeprecationsInfoMock, +})); + jest.doMock('./deprecations_factory', () => ({ DeprecationsFactory: DeprecationsFactoryMock, })); diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_service.test.ts b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_service.test.ts index 14a131ca8e563..39c299d980531 100644 --- a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_service.test.ts +++ b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_service.test.ts @@ -7,22 +7,24 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { DeprecationsFactoryMock } from './deprecations_service.test.mocks'; - +import { + DeprecationsFactoryMock, + registerConfigDeprecationsInfoMock, +} from './deprecations_service.test.mocks'; import { mockCoreContext } from '@kbn/core-base-server-mocks'; import { httpServiceMock } from '@kbn/core-http-server-mocks'; +import { coreUsageDataServiceMock } from '@kbn/core-usage-data-server-mocks'; import { configServiceMock } from '@kbn/config-mocks'; import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks'; import { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks'; import { DeprecationsService, DeprecationsSetupDeps } from './deprecations_service'; -import { mockDeprecationsRegistry, mockDeprecationsFactory } from './mocks'; -/* eslint-disable dot-notation */ describe('DeprecationsService', () => { let coreContext: ReturnType; let http: ReturnType; let router: ReturnType; let deprecationsCoreSetupDeps: DeprecationsSetupDeps; + let coreUsageData: ReturnType; beforeEach(() => { const configService = configServiceMock.create({ @@ -30,14 +32,16 @@ describe('DeprecationsService', () => { }); coreContext = mockCoreContext.create({ configService }); http = httpServiceMock.createInternalSetupContract(); + coreUsageData = coreUsageDataServiceMock.createSetupContract(); router = httpServiceMock.createRouter(); http.createRouter.mockReturnValue(router); - deprecationsCoreSetupDeps = { http }; + deprecationsCoreSetupDeps = { http, coreUsageData }; }); afterEach(() => { jest.clearAllMocks(); DeprecationsFactoryMock.mockClear(); + registerConfigDeprecationsInfoMock.mockClear(); }); describe('#setup', () => { @@ -53,10 +57,8 @@ describe('DeprecationsService', () => { it('calls registerConfigDeprecationsInfo', async () => { const deprecationsService = new DeprecationsService(coreContext); - const mockRegisterConfigDeprecationsInfo = jest.fn(); - deprecationsService['registerConfigDeprecationsInfo'] = mockRegisterConfigDeprecationsInfo; await deprecationsService.setup(deprecationsCoreSetupDeps); - expect(mockRegisterConfigDeprecationsInfo).toBeCalledTimes(1); + expect(registerConfigDeprecationsInfoMock).toBeCalledTimes(1); }); it('creates DeprecationsFactory with the correct parameters', async () => { @@ -89,92 +91,4 @@ describe('DeprecationsService', () => { }); }); }); - - describe('#registerConfigDeprecationsInfo', () => { - const deprecationsFactory = mockDeprecationsFactory.create(); - const deprecationsRegistry = mockDeprecationsRegistry.create(); - const getDeprecationsContext = mockDeprecationsRegistry.createGetDeprecationsContext(); - - it('registers config deprecations', async () => { - const deprecationsService = new DeprecationsService(coreContext); - coreContext.configService.getHandledDeprecatedConfigs.mockReturnValue([ - [ - 'testDomain', - [ - { - configPath: 'test', - level: 'critical', - message: 'testMessage', - documentationUrl: 'testDocUrl', - correctiveActions: { - manualSteps: [ - 'Using Kibana user management, change all users using the kibana_user role to the kibana_admin role.', - 'Using Kibana role-mapping management, change all role-mappings which assign the kibana_user role to the kibana_admin role.', - ], - }, - }, - ], - ], - ]); - - deprecationsFactory.getRegistry.mockReturnValue(deprecationsRegistry); - deprecationsService['registerConfigDeprecationsInfo'](deprecationsFactory); - - expect(coreContext.configService.getHandledDeprecatedConfigs).toBeCalledTimes(1); - expect(deprecationsFactory.getRegistry).toBeCalledTimes(1); - expect(deprecationsFactory.getRegistry).toBeCalledWith('testDomain'); - expect(deprecationsRegistry.registerDeprecations).toBeCalledTimes(1); - const configDeprecations = - await deprecationsRegistry.registerDeprecations.mock.calls[0][0].getDeprecations( - getDeprecationsContext - ); - expect(configDeprecations).toMatchInlineSnapshot(` - Array [ - Object { - "configPath": "test", - "correctiveActions": Object { - "manualSteps": Array [ - "Using Kibana user management, change all users using the kibana_user role to the kibana_admin role.", - "Using Kibana role-mapping management, change all role-mappings which assign the kibana_user role to the kibana_admin role.", - ], - }, - "deprecationType": "config", - "documentationUrl": "testDocUrl", - "level": "critical", - "message": "testMessage", - "requireRestart": true, - "title": "testDomain has a deprecated setting", - }, - ] - `); - }); - - it('accepts `level` field overrides', async () => { - const deprecationsService = new DeprecationsService(coreContext); - coreContext.configService.getHandledDeprecatedConfigs.mockReturnValue([ - [ - 'testDomain', - [ - { - configPath: 'test', - message: 'testMessage', - level: 'warning', - correctiveActions: { - manualSteps: ['step a'], - }, - }, - ], - ], - ]); - - deprecationsFactory.getRegistry.mockReturnValue(deprecationsRegistry); - deprecationsService['registerConfigDeprecationsInfo'](deprecationsFactory); - - const configDeprecations = - await deprecationsRegistry.registerDeprecations.mock.calls[0][0].getDeprecations( - getDeprecationsContext - ); - expect(configDeprecations[0].level).toBe('warning'); - }); - }); }); diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_service.ts b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_service.ts index 4c8f564943ab1..c0a0ef0f88c7b 100644 --- a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_service.ts +++ b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_service.ts @@ -19,9 +19,11 @@ import type { DeprecationRegistryProvider, DeprecationsClient, } from '@kbn/core-deprecations-server'; +import { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal'; import { DeprecationsFactory } from './deprecations_factory'; import { registerRoutes } from './routes'; import { config as deprecationConfig, DeprecationConfigType } from './deprecation_config'; +import { registerApiDeprecationsInfo, registerConfigDeprecationsInfo } from './deprecations'; export interface InternalDeprecationsServiceStart { /** @@ -40,6 +42,7 @@ export type InternalDeprecationsServiceSetup = DeprecationRegistryProvider; /** @internal */ export interface DeprecationsSetupDeps { http: InternalHttpServiceSetup; + coreUsageData: InternalCoreUsageDataSetup; } /** @internal */ @@ -55,7 +58,10 @@ export class DeprecationsService this.configService = coreContext.configService; } - public async setup({ http }: DeprecationsSetupDeps): Promise { + public async setup({ + http, + coreUsageData, + }: DeprecationsSetupDeps): Promise { this.logger.debug('Setting up Deprecations service'); const config = await firstValueFrom( @@ -69,8 +75,18 @@ export class DeprecationsService }, }); - registerRoutes({ http }); - this.registerConfigDeprecationsInfo(this.deprecationsFactory); + registerRoutes({ http, coreUsageData }); + + registerConfigDeprecationsInfo({ + deprecationsFactory: this.deprecationsFactory, + configService: this.configService, + }); + + registerApiDeprecationsInfo({ + deprecationsFactory: this.deprecationsFactory, + http, + coreUsageData, + }); const deprecationsFactory = this.deprecationsFactory; return { @@ -87,6 +103,7 @@ export class DeprecationsService if (!this.deprecationsFactory) { throw new Error('`setup` must be called before `start`'); } + return { asScopedToClient: this.createScopedDeprecations(), }; @@ -107,35 +124,4 @@ export class DeprecationsService }; }; } - - private registerConfigDeprecationsInfo(deprecationsFactory: DeprecationsFactory) { - const handledDeprecatedConfigs = this.configService.getHandledDeprecatedConfigs(); - - for (const [domainId, deprecationsContexts] of handledDeprecatedConfigs) { - const deprecationsRegistry = deprecationsFactory.getRegistry(domainId); - deprecationsRegistry.registerDeprecations({ - getDeprecations: () => { - return deprecationsContexts.map( - ({ - configPath, - title = `${domainId} has a deprecated setting`, - level, - message, - correctiveActions, - documentationUrl, - }) => ({ - configPath, - title, - level, - message, - correctiveActions, - documentationUrl, - deprecationType: 'config', - requireRestart: true, - }) - ); - }, - }); - } - } } diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/routes/index.ts b/packages/core/deprecations/core-deprecations-server-internal/src/routes/index.ts index e1a925610327f..f812bbfd15acd 100644 --- a/packages/core/deprecations/core-deprecations-server-internal/src/routes/index.ts +++ b/packages/core/deprecations/core-deprecations-server-internal/src/routes/index.ts @@ -8,10 +8,22 @@ */ import type { InternalHttpServiceSetup } from '@kbn/core-http-server-internal'; +import { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal'; import type { InternalDeprecationRequestHandlerContext } from '../internal_types'; import { registerGetRoute } from './get'; +import { registerMarkAsResolvedRoute } from './resolve_deprecated_api'; +import { registerApiDeprecationsPostValidationHandler } from './post_validation_handler'; -export function registerRoutes({ http }: { http: InternalHttpServiceSetup }) { +export function registerRoutes({ + http, + coreUsageData, +}: { + http: InternalHttpServiceSetup; + coreUsageData: InternalCoreUsageDataSetup; +}) { const router = http.createRouter('/api/deprecations'); registerGetRoute(router); + + registerApiDeprecationsPostValidationHandler({ http, coreUsageData }); + registerMarkAsResolvedRoute(router, { coreUsageData }); } diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/routes/post_validation_handler.ts b/packages/core/deprecations/core-deprecations-server-internal/src/routes/post_validation_handler.ts new file mode 100644 index 0000000000000..b93c17af2f536 --- /dev/null +++ b/packages/core/deprecations/core-deprecations-server-internal/src/routes/post_validation_handler.ts @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-server-internal'; +import type { CoreKibanaRequest } from '@kbn/core-http-router-server-internal'; +import type { InternalHttpServiceSetup } from '@kbn/core-http-server-internal'; +import { isObject } from 'lodash'; +import { RouteDeprecationInfo } from '@kbn/core-http-server/src/router/route'; +import { buildApiDeprecationId } from '../deprecations'; + +interface Dependencies { + coreUsageData: InternalCoreUsageDataSetup; + http: InternalHttpServiceSetup; +} + +/** + * listens to http post validation events to increment deprecated api calls + * This will keep track of any called deprecated API. + */ +export const registerApiDeprecationsPostValidationHandler = ({ + coreUsageData, + http, +}: Dependencies) => { + http.registerOnPostValidation(createRouteDeprecationsHandler({ coreUsageData })); +}; + +export function createRouteDeprecationsHandler({ + coreUsageData, +}: { + coreUsageData: InternalCoreUsageDataSetup; +}) { + return (req: CoreKibanaRequest, { deprecated }: { deprecated?: RouteDeprecationInfo }) => { + if (deprecated && isObject(deprecated) && req.route.routePath) { + const counterName = buildApiDeprecationId({ + routeMethod: req.route.method, + routePath: req.route.routePath, + routeVersion: req.apiVersion, + }); + + const client = coreUsageData.getClient(); + // no await we just fire it off. + void client.incrementDeprecatedApi(counterName, { resolved: false }); + } + }; +} diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/routes/resolve_deprecated_api.ts b/packages/core/deprecations/core-deprecations-server-internal/src/routes/resolve_deprecated_api.ts new file mode 100644 index 0000000000000..840bc5ac22d23 --- /dev/null +++ b/packages/core/deprecations/core-deprecations-server-internal/src/routes/resolve_deprecated_api.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { schema } from '@kbn/config-schema'; +import { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal'; +import type { InternalDeprecationRouter } from '../internal_types'; +import { buildApiDeprecationId } from '../deprecations'; + +export const registerMarkAsResolvedRoute = ( + router: InternalDeprecationRouter, + { coreUsageData }: { coreUsageData: InternalCoreUsageDataSetup } +) => { + router.post( + { + path: '/mark_as_resolved', + validate: { + body: schema.object({ + domainId: schema.string(), + routePath: schema.string(), + routeMethod: schema.oneOf([ + schema.literal('post'), + schema.literal('put'), + schema.literal('delete'), + schema.literal('patch'), + schema.literal('get'), + schema.literal('options'), + ]), + routeVersion: schema.maybe(schema.string()), + incrementBy: schema.number(), + }), + }, + }, + async (_, req, res) => { + const usageClient = coreUsageData.getClient(); + const { routeMethod, routePath, routeVersion, incrementBy } = req.body; + const counterName = buildApiDeprecationId({ + routeMethod, + routePath, + routeVersion, + }); + + await usageClient.incrementDeprecatedApi(counterName, { resolved: true, incrementBy }); + return res.ok(); + } + ); +}; diff --git a/packages/core/deprecations/core-deprecations-server-internal/tsconfig.json b/packages/core/deprecations/core-deprecations-server-internal/tsconfig.json index ba06a3e9ec2f7..02be6b7cb8198 100644 --- a/packages/core/deprecations/core-deprecations-server-internal/tsconfig.json +++ b/packages/core/deprecations/core-deprecations-server-internal/tsconfig.json @@ -33,6 +33,11 @@ "@kbn/core-elasticsearch-server-mocks", "@kbn/core-http-server", "@kbn/core-elasticsearch-client-server-mocks", + "@kbn/core-usage-data-base-server-internal", + "@kbn/core-usage-data-server", + "@kbn/core-usage-data-server-internal", + "@kbn/core-usage-data-server-mocks", + "@kbn/core-http-router-server-internal", ], "exclude": [ "target/**/*", diff --git a/packages/core/http/core-http-router-server-internal/index.ts b/packages/core/http/core-http-router-server-internal/index.ts index 6c684d5f8169c..6aa6ac117f533 100644 --- a/packages/core/http/core-http-router-server-internal/index.ts +++ b/packages/core/http/core-http-router-server-internal/index.ts @@ -13,16 +13,10 @@ export { CoreVersionedRouter, ALLOWED_PUBLIC_VERSION, unwrapVersionedResponseBodyValidation, - type VersionedRouterRoute, type HandlerResolutionStrategy, } from './src/versioned_router'; export { Router } from './src/router'; -export type { - RouterOptions, - InternalRegistrar, - InternalRegistrarOptions, - InternalRouterRoute, -} from './src/router'; +export type { RouterOptions, InternalRegistrar, InternalRegistrarOptions } from './src/router'; export { isKibanaRequest, isRealRequest, ensureRawRequest, CoreKibanaRequest } from './src/request'; export { isSafeMethod } from './src/route'; export { HapiResponseAdapter } from './src/response_adapter'; diff --git a/packages/core/http/core-http-router-server-internal/src/request.ts b/packages/core/http/core-http-router-server-internal/src/request.ts index 286b900fc24f5..9f89f1a70bb47 100644 --- a/packages/core/http/core-http-router-server-internal/src/request.ts +++ b/packages/core/http/core-http-router-server-internal/src/request.ts @@ -143,6 +143,8 @@ export class CoreKibanaRequest< public readonly rewrittenUrl?: URL; /** {@inheritDoc KibanaRequest.httpVersion} */ public readonly httpVersion: string; + /** {@inheritDoc KibanaRequest.apiVersion} */ + public readonly apiVersion: undefined; /** {@inheritDoc KibanaRequest.protocol} */ public readonly protocol: HttpProtocol; /** {@inheritDoc KibanaRequest.authzResult} */ @@ -185,6 +187,7 @@ export class CoreKibanaRequest< }); this.httpVersion = isRealReq ? request.raw.req.httpVersion : '1.0'; + this.apiVersion = undefined; this.protocol = getProtocolFromHttpVersion(this.httpVersion); this.route = deepFreeze(this.getRouteInfo(request)); @@ -216,6 +219,7 @@ export class CoreKibanaRequest< }, route: this.route, authzResult: this.authzResult, + apiVersion: this.apiVersion, }; } @@ -252,7 +256,14 @@ export class CoreKibanaRequest< } = request.route?.settings?.payload || {}; // the socket is undefined when using @hapi/shot, or when a "fake request" is used - const socketTimeout = isRealRawRequest(request) ? request.raw.req.socket?.timeout : undefined; + let socketTimeout: undefined | number; + let routePath: undefined | string; + + if (isRealRawRequest(request)) { + socketTimeout = request.raw.req.socket?.timeout; + routePath = request.route.path; + } + const options = { authRequired: this.getAuthRequired(request), // TypeScript note: Casting to `RouterOptions` to fix the following error: @@ -266,6 +277,8 @@ export class CoreKibanaRequest< xsrfRequired: ((request.route?.settings as RouteOptions)?.app as KibanaRouteOptions)?.xsrfRequired ?? true, // some places in LP call KibanaRequest.from(request) manually. remove fallback to true before v8 + deprecated: ((request.route?.settings as RouteOptions)?.app as KibanaRouteOptions) + ?.deprecated, access: this.getAccess(request), tags: request.route?.settings?.tags || [], security: this.getSecurity(request), @@ -285,6 +298,7 @@ export class CoreKibanaRequest< return { path: request.path ?? '/', + routePath, method, options, }; diff --git a/packages/core/http/core-http-router-server-internal/src/router.test.ts b/packages/core/http/core-http-router-server-internal/src/router.test.ts index c318e9312546a..2c702fb3ef702 100644 --- a/packages/core/http/core-http-router-server-internal/src/router.test.ts +++ b/packages/core/http/core-http-router-server-internal/src/router.test.ts @@ -50,10 +50,18 @@ describe('Router', () => { path: '/', validate: { body: validation, query: validation, params: validation }, options: { - deprecated: true, + deprecated: { + documentationUrl: 'https://fake-url.com', + reason: { type: 'remove' }, + severity: 'warning', + }, discontinued: 'post test discontinued', summary: 'post test summary', description: 'post test description', + availability: { + since: '1.0.0', + stability: 'experimental', + }, }, }, (context, req, res) => res.ok() @@ -68,10 +76,18 @@ describe('Router', () => { validationSchemas: { body: validation, query: validation, params: validation }, isVersioned: false, options: { - deprecated: true, + deprecated: { + documentationUrl: 'https://fake-url.com', + reason: { type: 'remove' }, + severity: 'warning', + }, discontinued: 'post test discontinued', summary: 'post test summary', description: 'post test description', + availability: { + since: '1.0.0', + stability: 'experimental', + }, }, }); }); @@ -85,7 +101,7 @@ describe('Router', () => { validate: { body: validation, query: validation, params: validation }, }, (context, req, res) => res.ok(), - { isVersioned: true } + { isVersioned: true, events: false } ); router.get( { diff --git a/packages/core/http/core-http-router-server-internal/src/router.ts b/packages/core/http/core-http-router-server-internal/src/router.ts index 36f324236a4d2..e7ad85fcda33a 100644 --- a/packages/core/http/core-http-router-server-internal/src/router.ts +++ b/packages/core/http/core-http-router-server-internal/src/router.ts @@ -7,6 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import { EventEmitter } from 'node:events'; import type { Request, ResponseToolkit } from '@hapi/hapi'; import apm from 'elastic-apm-node'; import { isConfigSchema } from '@kbn/config-schema'; @@ -32,6 +33,7 @@ import { isZod } from '@kbn/zod'; import { validBodyOutput, getRequestValidation } from '@kbn/core-http-server'; import type { RouteSecurityGetter } from '@kbn/core-http-server'; import type { DeepPartial } from '@kbn/utility-types'; +import { RouteDeprecationInfo } from '@kbn/core-http-server/src/router/route'; import { RouteValidator } from './validator'; import { ALLOWED_PUBLIC_VERSION, CoreVersionedRouter } from './versioned_router'; import { CoreKibanaRequest } from './request'; @@ -52,7 +54,7 @@ export type ContextEnhancer< Context extends RequestHandlerContextBase > = (handler: RequestHandler) => RequestHandlerEnhanced; -function getRouteFullPath(routerPath: string, routePath: string) { +export function getRouteFullPath(routerPath: string, routePath: string) { // If router's path ends with slash and route's path starts with slash, // we should omit one of them to have a valid concatenated path. const routePathStartIndex = routerPath.endsWith('/') && routePath.startsWith('/') ? 1 : 0; @@ -147,7 +149,13 @@ export interface RouterOptions { /** @internal */ export interface InternalRegistrarOptions { + /** @default false */ isVersioned: boolean; + /** + * Whether this route should emit "route events" like postValidate + * @default true + */ + events: boolean; } /** @internal */ @@ -166,15 +174,9 @@ export type InternalRegistrar ReturnType>; /** @internal */ -export interface InternalRouterRoute extends RouterRoute { - readonly isVersioned: boolean; -} - -/** @internal */ -interface InternalGetRoutesOptions { - /** @default false */ - excludeVersionedRoutes?: boolean; -} +type RouterEvents = + /** Called after route validation, regardless of success or failure */ + 'onPostValidate'; /** * @internal @@ -182,7 +184,8 @@ interface InternalGetRoutesOptions { export class Router implements IRouter { - public routes: Array> = []; + private static ee = new EventEmitter(); + public routes: Array> = []; public pluginId?: symbol; public get: InternalRegistrar<'get', Context>; public post: InternalRegistrar<'post', Context>; @@ -202,25 +205,27 @@ export class Router( route: InternalRouteConfig, handler: RequestHandler, - { isVersioned }: InternalRegistrarOptions = { isVersioned: false } + { isVersioned, events }: InternalRegistrarOptions = { isVersioned: false, events: true } ) => { route = prepareRouteConfigValidation(route); const routeSchemas = routeSchemasFromRouteConfig(route, method); - const isPublicUnversionedApi = + const isPublicUnversionedRoute = !isVersioned && route.options?.access === 'public' && // We do not consider HTTP resource routes as APIs route.options?.httpResource !== true; this.routes.push({ - handler: async (req, responseToolkit) => - await this.handle({ + handler: async (req, responseToolkit) => { + return await this.handle({ routeSchemas, request: req, responseToolkit, - isPublicUnversionedApi, + isPublicUnversionedRoute, handler: this.enhanceWithContext(handler), - }), + emit: events ? { onPostValidation: this.emitPostValidate } : undefined, + }); + }, method, path: getRouteFullPath(this.routerPath, route.path), options: validOptions(method, route), @@ -229,6 +234,8 @@ export class Router, route.options), validationSchemas: route.validate, + // @ts-ignore using isVersioned: false in the type instead of boolean + // for typeguarding between versioned and unversioned RouterRoute types isVersioned, }); }; @@ -240,7 +247,15 @@ export class Router void) { + Router.ee.on(event, cb); + } + + public static off(event: RouterEvents, cb: (req: CoreKibanaRequest, ...args: any[]) => void) { + Router.ee.off(event, cb); + } + + public getRoutes({ excludeVersionedRoutes }: { excludeVersionedRoutes?: boolean } = {}) { if (excludeVersionedRoutes) { return this.routes.filter((route) => !route.isVersioned); } @@ -269,16 +284,29 @@ export class Router { + const postValidate: RouterEvents = 'onPostValidate'; + Router.ee.emit(postValidate, request, routeOptions); + }; + private async handle({ routeSchemas, request, responseToolkit, - isPublicUnversionedApi, + emit, + isPublicUnversionedRoute, handler, }: { request: Request; responseToolkit: ResponseToolkit; - isPublicUnversionedApi: boolean; + emit?: { + onPostValidation: (req: KibanaRequest, reqOptions: any) => void; + }; + isPublicUnversionedRoute: boolean; handler: RequestHandlerEnhanced< P, Q, @@ -300,18 +328,24 @@ export class Router { it('should pass validation for valid route security with authz enabled and valid required privileges', () => { @@ -276,4 +277,31 @@ describe('RouteSecurity validation', () => { `"[authz.requiredPrivileges]: anyRequired privileges must contain unique values"` ); }); + + it('should fail validation when anyRequired has superuser privileges set', () => { + const invalidRouteSecurity = { + authz: { + requiredPrivileges: [ + { anyRequired: ['privilege1', 'privilege1'], allRequired: ['privilege4'] }, + { anyRequired: ['privilege5', ReservedPrivilegesSet.superuser] }, + ], + }, + }; + + expect(() => validRouteSecurity(invalidRouteSecurity)).toThrowErrorMatchingInlineSnapshot( + `"[authz.requiredPrivileges]: Combining superuser with other privileges is redundant, superuser privileges set can be only used as a standalone privilege."` + ); + }); + + it('should fail validation when allRequired combines superuser privileges set with other privileges', () => { + const invalidRouteSecurity = { + authz: { + requiredPrivileges: [ReservedPrivilegesSet.superuser, 'privilege1'], + }, + }; + + expect(() => validRouteSecurity(invalidRouteSecurity)).toThrowErrorMatchingInlineSnapshot( + `"[authz.requiredPrivileges]: Combining superuser with other privileges is redundant, superuser privileges set can be only used as a standalone privilege."` + ); + }); }); diff --git a/packages/core/http/core-http-router-server-internal/src/security_route_config_validator.ts b/packages/core/http/core-http-router-server-internal/src/security_route_config_validator.ts index d74f41d3157b4..65073f9a66ec6 100644 --- a/packages/core/http/core-http-router-server-internal/src/security_route_config_validator.ts +++ b/packages/core/http/core-http-router-server-internal/src/security_route_config_validator.ts @@ -9,6 +9,7 @@ import { schema } from '@kbn/config-schema'; import type { RouteSecurity, RouteConfigOptions } from '@kbn/core-http-server'; +import { ReservedPrivilegesSet } from '@kbn/core-http-server'; import type { DeepPartial } from '@kbn/utility-types'; const privilegeSetSchema = schema.object( @@ -49,6 +50,15 @@ const requiredPrivilegesSchema = schema.arrayOf( } }); + // Combining superuser with other privileges is redundant. + // If user is a superuser, they inherently have access to all the privileges that may come with other roles. + if ( + anyRequired.includes(ReservedPrivilegesSet.superuser) || + (allRequired.includes(ReservedPrivilegesSet.superuser) && allRequired.length > 1) + ) { + return 'Combining superuser with other privileges is redundant, superuser privileges set can be only used as a standalone privilege.'; + } + if (anyRequired.length && allRequired.length) { for (const privilege of anyRequired) { if (allRequired.includes(privilege)) { diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.test.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.test.ts index 3938b8addfc25..0ecb65ee76804 100644 --- a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.test.ts +++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.test.ts @@ -52,6 +52,46 @@ describe('Versioned route', () => { jest.clearAllMocks(); }); + describe('#getRoutes', () => { + it('returns the expected metadata', () => { + const versionedRouter = CoreVersionedRouter.from({ router }); + versionedRouter + .get({ + path: '/test/{id}', + access: 'public', + options: { + httpResource: true, + availability: { + since: '1.0.0', + stability: 'experimental', + }, + excludeFromOAS: true, + tags: ['1', '2', '3'], + }, + description: 'test', + summary: 'test', + enableQueryVersion: false, + }) + .addVersion({ version: '2023-10-31', validate: false }, handlerFn); + + expect(versionedRouter.getRoutes()[0].options).toMatchObject({ + access: 'public', + enableQueryVersion: false, + description: 'test', + summary: 'test', + options: { + httpResource: true, + availability: { + since: '1.0.0', + stability: 'experimental', + }, + excludeFromOAS: true, + tags: ['1', '2', '3'], + }, + }); + }); + }); + it('can register multiple handlers', () => { const versionedRouter = CoreVersionedRouter.from({ router }); versionedRouter @@ -133,6 +173,8 @@ describe('Versioned route', () => { const opts: Parameters[0] = { path: '/test/{id}', access: 'internal', + summary: 'test', + description: 'test', options: { authRequired: true, tags: ['access:test'], @@ -140,7 +182,6 @@ describe('Versioned route', () => { xsrfRequired: false, excludeFromOAS: true, httpResource: true, - summary: `test`, }, }; @@ -157,7 +198,7 @@ describe('Versioned route', () => { expect(router.post).toHaveBeenCalledWith( expect.objectContaining(expectedRouteConfig), expect.any(Function), - { isVersioned: true } + { isVersioned: true, events: false } ); }); diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.ts index e9a9e60de8193..45654696ba0cf 100644 --- a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.ts +++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.ts @@ -18,7 +18,6 @@ import type { KibanaRequest, KibanaResponseFactory, ApiVersion, - AddVersionOpts, VersionedRoute, VersionedRouteConfig, IKibanaResponse, @@ -26,9 +25,10 @@ import type { RouteSecurityGetter, RouteSecurity, RouteMethod, + VersionedRouterRoute, } from '@kbn/core-http-server'; import type { Mutable } from 'utility-types'; -import type { HandlerResolutionStrategy, Method, VersionedRouterRoute } from './types'; +import type { HandlerResolutionStrategy, Method, Options } from './types'; import { validate } from './validate'; import { @@ -46,8 +46,6 @@ import { prepareVersionedRouteValidation, unwrapVersionedResponseBodyValidation import type { RequestLike } from './route_version_utils'; import { Router } from '../router'; -type Options = AddVersionOpts; - interface InternalVersionedRouteConfig extends VersionedRouteConfig { isDev: boolean; useVersionResolutionStrategyForInternalPaths: Map; @@ -68,7 +66,7 @@ function extractValidationSchemaFromHandler(handler: VersionedRouterRoute['handl } export class CoreVersionedRoute implements VersionedRoute { - private readonly handlers = new Map< + public readonly handlers = new Map< ApiVersion, { fn: RequestHandler; @@ -127,7 +125,7 @@ export class CoreVersionedRoute implements VersionedRoute { security: this.getSecurity, }, this.requestHandler, - { isVersioned: true } + { isVersioned: true, events: false } ); } @@ -181,6 +179,7 @@ export class CoreVersionedRoute implements VersionedRoute { } const req = originalReq as Mutable; const version = this.getVersion(req); + req.apiVersion = version; if (!version) { return res.badRequest({ @@ -221,6 +220,8 @@ export class CoreVersionedRoute implements VersionedRoute { req.params = params; req.query = query; } catch (e) { + // Emit onPostValidation even if validation fails. + this.router.emitPostValidate(req, handler.options.options); return res.badRequest({ body: e.message, headers: getVersionHeader(version) }); } } else { @@ -230,6 +231,8 @@ export class CoreVersionedRoute implements VersionedRoute { req.query = {}; } + this.router.emitPostValidate(req, handler.options.options); + const response = await handler.fn(ctx, req, res); if (this.isDev && validation?.response?.[response.status]?.body) { @@ -280,7 +283,6 @@ export class CoreVersionedRoute implements VersionedRoute { public addVersion(options: Options, handler: RequestHandler): VersionedRoute { this.validateVersion(options.version); options = prepareVersionedRouteValidation(options); - this.handlers.set(options.version, { fn: handler, options, diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.test.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.test.ts index d56de36ba9a29..a3ffffc0ef219 100644 --- a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.test.ts +++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.test.ts @@ -36,7 +36,6 @@ describe('Versioned router', () => { versionedRouter.get({ path: '/test/{id}', access: 'internal', - deprecated: true, discontinued: 'x.y.z', }); versionedRouter.post({ @@ -50,16 +49,17 @@ describe('Versioned router', () => { Array [ Object { "handlers": Array [], + "isVersioned": true, "method": "get", "options": Object { "access": "internal", - "deprecated": true, "discontinued": "x.y.z", }, "path": "/test/{id}", }, Object { "handlers": Array [], + "isVersioned": true, "method": "post", "options": Object { "access": "internal", @@ -70,6 +70,7 @@ describe('Versioned router', () => { }, Object { "handlers": Array [], + "isVersioned": true, "method": "delete", "options": Object { "access": "internal", diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.ts index e9272e17ab18e..ef1f8255420ae 100644 --- a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.ts +++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.ts @@ -7,11 +7,16 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import type { VersionedRouter, VersionedRoute, VersionedRouteConfig } from '@kbn/core-http-server'; +import type { + VersionedRouter, + VersionedRoute, + VersionedRouteConfig, + VersionedRouterRoute, +} from '@kbn/core-http-server'; import { omit } from 'lodash'; import { CoreVersionedRoute } from './core_versioned_route'; -import type { HandlerResolutionStrategy, Method, VersionedRouterRoute } from './types'; -import type { Router } from '../router'; +import type { HandlerResolutionStrategy, Method } from './types'; +import { getRouteFullPath, type Router } from '../router'; /** @internal */ export interface VersionedRouterArgs { @@ -98,10 +103,11 @@ export class CoreVersionedRouter implements VersionedRouter { public getRoutes(): VersionedRouterRoute[] { return [...this.routes].map((route) => { return { - path: route.path, + path: getRouteFullPath(this.router.routerPath, route.path), method: route.method, options: omit(route.options, 'path'), handlers: route.getHandlers(), + isVersioned: true, }; }); } diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/index.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/index.ts index e283fcc2a590f..14c08076faae0 100644 --- a/packages/core/http/core-http-router-server-internal/src/versioned_router/index.ts +++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/index.ts @@ -9,6 +9,6 @@ export { resolvers as versionHandlerResolvers } from './handler_resolvers'; export { CoreVersionedRouter } from './core_versioned_router'; -export type { HandlerResolutionStrategy, VersionedRouterRoute } from './types'; +export type { HandlerResolutionStrategy } from './types'; export { ALLOWED_PUBLIC_VERSION } from './route_version_utils'; export { unwrapVersionedResponseBodyValidation } from './util'; diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/mocks.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/mocks.ts index 5a958fa9251f7..36a672ca6a9f7 100644 --- a/packages/core/http/core-http-router-server-internal/src/versioned_router/mocks.ts +++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/mocks.ts @@ -20,6 +20,7 @@ export function createRouter(opts: CreateMockRouterOptions = {}) { put: jest.fn(), getRoutes: jest.fn(), handleLegacyErrors: jest.fn(), + emitPostValidate: jest.fn(), patch: jest.fn(), routerPath: '', versioned: {} as any, diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/types.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/types.ts index aec1f8b0cf0ab..bdcaae486cd9c 100644 --- a/packages/core/http/core-http-router-server-internal/src/versioned_router/types.ts +++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/types.ts @@ -7,25 +7,12 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import type { - AddVersionOpts, - RequestHandler, - RouteMethod, - VersionedRouteConfig, -} from '@kbn/core-http-server'; +import type { AddVersionOpts, RouteMethod } from '@kbn/core-http-server'; export type Method = Exclude; /** @internal */ -export interface VersionedRouterRoute { - method: string; - path: string; - options: Omit, 'path'>; - handlers: Array<{ - fn: RequestHandler; - options: AddVersionOpts; - }>; -} +export type Options = AddVersionOpts; /** * Specifies resolution strategy to use if a request does not provide a version. diff --git a/packages/core/http/core-http-router-server-mocks/src/versioned_router.mock.ts b/packages/core/http/core-http-router-server-mocks/src/versioned_router.mock.ts index 987288cf372bd..fec80b06963a0 100644 --- a/packages/core/http/core-http-router-server-mocks/src/versioned_router.mock.ts +++ b/packages/core/http/core-http-router-server-mocks/src/versioned_router.mock.ts @@ -14,6 +14,7 @@ import type { AddVersionOpts, RequestHandler, KibanaResponseFactory, + VersionedRouterRoute, } from '@kbn/core-http-server'; export type MockedVersionedRoute = jest.Mocked; @@ -24,14 +25,16 @@ const createMockVersionedRoute = (): MockedVersionedRoute => { return api; }; +type VersionedRouterMethods = keyof Omit; + export type MockedVersionedRouter = jest.Mocked> & { - getRoute: (method: keyof VersionedRouter, path: string) => RegisteredVersionedRoute; + getRoute: (method: VersionedRouterMethods, path: string) => RegisteredVersionedRoute; }; const createMethodHandler = () => jest.fn((_) => createMockVersionedRoute()); - +const createMockGetRoutes = () => jest.fn(() => [] as VersionedRouterRoute[]); export const createVersionedRouterMock = (): MockedVersionedRouter => { - const router: Omit = { + const router: Omit = { delete: createMethodHandler(), get: createMethodHandler(), patch: createMethodHandler(), @@ -42,6 +45,7 @@ export const createVersionedRouterMock = (): MockedVersionedRouter => { return { ...router, getRoute: getRoute.bind(null, router), + getRoutes: createMockGetRoutes(), }; }; @@ -54,9 +58,10 @@ export interface RegisteredVersionedRoute { }; }; } + const getRoute = ( - router: Omit, - method: keyof VersionedRouter, + router: Omit, + method: VersionedRouterMethods, path: string ): RegisteredVersionedRoute => { if (!router[method].mock.calls.length) { diff --git a/packages/core/http/core-http-server-internal/src/http_server.test.ts b/packages/core/http/core-http-server-internal/src/http_server.test.ts index c374ff7ca2107..69e69f784e65e 100644 --- a/packages/core/http/core-http-server-internal/src/http_server.test.ts +++ b/packages/core/http/core-http-server-internal/src/http_server.test.ts @@ -906,6 +906,7 @@ test('exposes route details of incoming request to a route handler', async () => .expect(200, { method: 'get', path: '/', + routePath: '/', options: { authRequired: true, xsrfRequired: false, @@ -1088,6 +1089,7 @@ test('exposes route details of incoming request to a route handler (POST + paylo .expect(200, { method: 'post', path: '/', + routePath: '/', options: { authRequired: true, xsrfRequired: true, diff --git a/packages/core/http/core-http-server-internal/src/http_server.ts b/packages/core/http/core-http-server-internal/src/http_server.ts index 46470bac7c504..1dee6a3286788 100644 --- a/packages/core/http/core-http-server-internal/src/http_server.ts +++ b/packages/core/http/core-http-server-internal/src/http_server.ts @@ -35,10 +35,12 @@ import type { HttpServerInfo, HttpAuth, IAuthHeadersStorage, + RouterDeprecatedRouteDetails, + RouteMethod, } from '@kbn/core-http-server'; import { performance } from 'perf_hooks'; import { isBoom } from '@hapi/boom'; -import { identity } from 'lodash'; +import { identity, isObject } from 'lodash'; import { IHttpEluMonitorConfig } from '@kbn/core-http-server/src/elu_monitor'; import { Env } from '@kbn/config'; import { CoreContext } from '@kbn/core-base-server-internal'; @@ -140,6 +142,7 @@ export interface HttpServerSetup { registerAuth: HttpServiceSetup['registerAuth']; registerOnPostAuth: HttpServiceSetup['registerOnPostAuth']; registerOnPreResponse: HttpServiceSetup['registerOnPreResponse']; + getDeprecatedRoutes: HttpServiceSetup['getDeprecatedRoutes']; authRequestHeaders: IAuthHeadersStorage; auth: HttpAuth; getServerInfo: () => HttpServerInfo; @@ -280,6 +283,7 @@ export class HttpServer { return { registerRouter: this.registerRouter.bind(this), + getDeprecatedRoutes: this.getDeprecatedRoutes.bind(this), registerRouterAfterListening: this.registerRouterAfterListening.bind(this), registerStaticDir: this.registerStaticDir.bind(this), staticAssets, @@ -385,6 +389,45 @@ export class HttpServer { } } + private getDeprecatedRoutes(): RouterDeprecatedRouteDetails[] { + const deprecatedRoutes: RouterDeprecatedRouteDetails[] = []; + + for (const router of this.registeredRouters) { + const allRouterRoutes = [ + // exclude so we dont get double entries. + // we need to call the versioned getRoutes to grab the full version options details + router.getRoutes({ excludeVersionedRoutes: true }), + router.versioned.getRoutes(), + ].flat(); + + deprecatedRoutes.push( + ...allRouterRoutes + .flat() + .map((route) => { + if (route.isVersioned === true) { + return [...route.handlers.entries()].map(([_, { options }]) => { + const deprecated = options.options?.deprecated; + return { route, version: `${options.version}`, deprecated }; + }); + } + return { route, version: undefined, deprecated: route.options.deprecated }; + }) + .flat() + .filter(({ deprecated }) => isObject(deprecated)) + .flatMap(({ route, deprecated, version }) => { + return { + routeDeprecationOptions: deprecated!, + routeMethod: route.method as RouteMethod, + routePath: route.path, + routeVersion: version, + }; + }) + ); + } + + return deprecatedRoutes; + } + private setupGracefulShutdownHandlers() { this.registerOnPreRouting((request, response, toolkit) => { if (this.stopping || this.stopped) { @@ -693,12 +736,13 @@ export class HttpServer { this.log.debug(`registering route handler for [${route.path}]`); // Hapi does not allow payload validation to be specified for 'head' or 'get' requests const validate = isSafeMethod(route.method) ? undefined : { payload: true }; - const { authRequired, tags, body = {}, timeout } = route.options; + const { authRequired, tags, body = {}, timeout, deprecated } = route.options; const { accepts: allow, override, maxBytes, output, parse } = body; const kibanaRouteOptions: KibanaRouteOptions = { xsrfRequired: route.options.xsrfRequired ?? !isSafeMethod(route.method), access: route.options.access ?? 'internal', + deprecated, security: route.security, }; // Log HTTP API target consumer. diff --git a/packages/core/http/core-http-server-internal/src/http_service.ts b/packages/core/http/core-http-server-internal/src/http_service.ts index 3f803b06f15fd..af310a6792057 100644 --- a/packages/core/http/core-http-server-internal/src/http_service.ts +++ b/packages/core/http/core-http-server-internal/src/http_service.ts @@ -162,7 +162,7 @@ export class HttpService return this.internalPreboot; } - public async setup(deps: SetupDeps) { + public async setup(deps: SetupDeps): Promise { this.requestHandlerContext = deps.context.createContextContainer(); this.configSubscription = this.config$.subscribe(() => { if (this.httpServer.isListening()) { @@ -185,9 +185,11 @@ export class HttpService this.internalSetup = { ...serverContract, - + registerOnPostValidation: (cb) => { + Router.on('onPostValidate', cb); + }, + getRegisteredDeprecatedApis: () => serverContract.getDeprecatedRoutes(), externalUrl: new ExternalUrlConfig(config.externalUrl), - createRouter: ( path: string, pluginId: PluginOpaqueId = this.coreContext.coreId diff --git a/packages/core/http/core-http-server-internal/src/static_assets/util.ts b/packages/core/http/core-http-server-internal/src/static_assets/util.ts index 9cd9213805b23..0bcc738582f2b 100644 --- a/packages/core/http/core-http-server-internal/src/static_assets/util.ts +++ b/packages/core/http/core-http-server-internal/src/static_assets/util.ts @@ -14,11 +14,23 @@ function isEmptyPathname(pathname: string): boolean { } function removeTailSlashes(pathname: string): string { - return pathname.replace(/\/+$/, ''); + let updated = pathname; + + while (updated.endsWith('/')) { + updated = updated.substring(0, updated.length - 1); + } + + return updated; } function removeLeadSlashes(pathname: string): string { - return pathname.replace(/^\/+/, ''); + let updated = pathname; + + while (updated.startsWith('/')) { + updated = updated.substring(1); + } + + return updated; } export function removeSurroundingSlashes(pathname: string): string { diff --git a/packages/core/http/core-http-server-internal/src/types.ts b/packages/core/http/core-http-server-internal/src/types.ts index 70dde23f035d0..0706af9ad73a2 100644 --- a/packages/core/http/core-http-server-internal/src/types.ts +++ b/packages/core/http/core-http-server-internal/src/types.ts @@ -16,7 +16,10 @@ import type { IContextContainer, HttpServiceSetup, HttpServiceStart, + RouterDeprecatedRouteDetails, } from '@kbn/core-http-server'; +import { CoreKibanaRequest } from '@kbn/core-http-router-server-internal'; +import { RouteDeprecationInfo } from '@kbn/core-http-server/src/router/route'; import type { HttpServerSetup } from './http_server'; import type { ExternalUrlConfig } from './external_url'; import type { InternalStaticAssets } from './static_assets'; @@ -54,6 +57,9 @@ export interface InternalHttpServiceSetup path: string, plugin?: PluginOpaqueId ) => IRouter; + registerOnPostValidation( + cb: (req: CoreKibanaRequest, metadata: { deprecated: RouteDeprecationInfo }) => void + ): void; registerRouterAfterListening: (router: IRouter) => void; registerStaticDir: (path: string, dirPath: string) => void; authRequestHeaders: IAuthHeadersStorage; @@ -65,6 +71,7 @@ export interface InternalHttpServiceSetup contextName: ContextName, provider: IContextProvider ) => IContextContainer; + getRegisteredDeprecatedApis: () => RouterDeprecatedRouteDetails[]; } /** @internal */ diff --git a/packages/core/http/core-http-server-mocks/src/http_service.mock.ts b/packages/core/http/core-http-server-mocks/src/http_service.mock.ts index 4e803ee5f86a8..116db3648f120 100644 --- a/packages/core/http/core-http-server-mocks/src/http_service.mock.ts +++ b/packages/core/http/core-http-server-mocks/src/http_service.mock.ts @@ -171,6 +171,9 @@ const createInternalSetupContractMock = () => { createCookieSessionStorageFactory: jest.fn(), registerOnPreRouting: jest.fn(), registerOnPreAuth: jest.fn(), + getDeprecatedRoutes: jest.fn(), + getRegisteredDeprecatedApis: jest.fn(), + registerOnPostValidation: jest.fn(), registerAuth: jest.fn(), registerOnPostAuth: jest.fn(), registerRouteHandlerContext: jest.fn(), @@ -207,6 +210,7 @@ const createSetupContractMock = < createCookieSessionStorageFactory: internalMock.createCookieSessionStorageFactory, registerOnPreRouting: internalMock.registerOnPreRouting, registerOnPreAuth: jest.fn(), + getDeprecatedRoutes: jest.fn(), registerAuth: internalMock.registerAuth, registerOnPostAuth: internalMock.registerOnPostAuth, registerOnPreResponse: internalMock.registerOnPreResponse, diff --git a/packages/core/http/core-http-server/index.ts b/packages/core/http/core-http-server/index.ts index 4ba653fbd534c..9c12f6a09ac45 100644 --- a/packages/core/http/core-http-server/index.ts +++ b/packages/core/http/core-http-server/index.ts @@ -93,6 +93,7 @@ export type { IRouter, RouteRegistrar, RouterRoute, + RouterDeprecatedRouteDetails, IKibanaSocket, KibanaErrorResponseFactory, KibanaRedirectionResponseFactory, @@ -127,6 +128,7 @@ export { getResponseValidation, isFullValidatorContainer, isKibanaResponse, + ReservedPrivilegesSet, } from './src/router'; export type { ICspConfig } from './src/csp'; @@ -170,6 +172,7 @@ export type { VersionedRouter, VersionedRouteCustomResponseBodyValidation, VersionedResponseBodyValidation, + VersionedRouterRoute, } from './src/versioning'; export type { IStaticAssets } from './src/static_assets'; diff --git a/packages/core/http/core-http-server/src/http_contract.ts b/packages/core/http/core-http-server/src/http_contract.ts index 72eb70149f529..e2f675bd8d0c0 100644 --- a/packages/core/http/core-http-server/src/http_contract.ts +++ b/packages/core/http/core-http-server/src/http_contract.ts @@ -12,6 +12,7 @@ import type { IContextProvider, IRouter, RequestHandlerContextBase, + RouterDeprecatedRouteDetails, } from './router'; import type { AuthenticationHandler, @@ -359,6 +360,14 @@ export interface HttpServiceSetup< * Provides common {@link HttpServerInfo | information} about the running http server. */ getServerInfo: () => HttpServerInfo; + + /** + * Provides a list of all registered deprecated routes {{@link RouterDeprecatedRouteDetails | information}}. + * The routers will be evaluated everytime this function gets called to + * accommodate for any late route registrations + * @returns {RouterDeprecatedRouteDetails[]} + */ + getDeprecatedRoutes: () => RouterDeprecatedRouteDetails[]; } /** @public */ diff --git a/packages/core/http/core-http-server/src/router/index.ts b/packages/core/http/core-http-server/src/router/index.ts index c26212fa0de81..8e2b9373c43bd 100644 --- a/packages/core/http/core-http-server/src/router/index.ts +++ b/packages/core/http/core-http-server/src/router/index.ts @@ -66,7 +66,7 @@ export type { PrivilegeSet, } from './route'; -export { validBodyOutput } from './route'; +export { validBodyOutput, ReservedPrivilegesSet } from './route'; export type { RouteValidationFunction, RouteValidationResultFactory, @@ -80,7 +80,7 @@ export type { LazyValidator, } from './route_validator'; export { RouteValidationError } from './route_validator'; -export type { IRouter, RouteRegistrar, RouterRoute } from './router'; +export type { IRouter, RouteRegistrar, RouterRoute, RouterDeprecatedRouteDetails } from './router'; export type { IKibanaSocket } from './socket'; export type { KibanaErrorResponseFactory, diff --git a/packages/core/http/core-http-server/src/router/request.ts b/packages/core/http/core-http-server/src/router/request.ts index 5cb84a21be0c3..066372faca1e4 100644 --- a/packages/core/http/core-http-server/src/router/request.ts +++ b/packages/core/http/core-http-server/src/router/request.ts @@ -13,7 +13,7 @@ import type { Observable } from 'rxjs'; import type { RecursiveReadonly } from '@kbn/utility-types'; import type { HttpProtocol } from '../http_contract'; import type { IKibanaSocket } from './socket'; -import type { RouteMethod, RouteConfigOptions, RouteSecurity } from './route'; +import type { RouteMethod, RouteConfigOptions, RouteSecurity, RouteDeprecationInfo } from './route'; import type { Headers } from './headers'; export type RouteSecurityGetter = (request: { @@ -26,6 +26,7 @@ export type InternalRouteSecurity = RouteSecurity | RouteSecurityGetter; * @public */ export interface KibanaRouteOptions extends RouteOptionsApp { + deprecated?: RouteDeprecationInfo; xsrfRequired: boolean; access: 'internal' | 'public'; security?: InternalRouteSecurity; @@ -59,6 +60,7 @@ export interface KibanaRequestRoute { path: string; method: Method; options: KibanaRequestRouteOptions; + routePath?: string; } /** @@ -190,6 +192,11 @@ export interface KibanaRequest< */ readonly rewrittenUrl?: URL; + /** + * The versioned route API version of this request. + */ + readonly apiVersion: string | undefined; + /** * The path parameter of this request. */ diff --git a/packages/core/http/core-http-server/src/router/route.ts b/packages/core/http/core-http-server/src/router/route.ts index a97ff9dd4040b..17fecd1c48b17 100644 --- a/packages/core/http/core-http-server/src/router/route.ts +++ b/packages/core/http/core-http-server/src/router/route.ts @@ -113,6 +113,43 @@ export type RouteAccess = 'public' | 'internal'; export type Privilege = string; +/** + * Route Deprecation info + * This information will assist Kibana HTTP API users when upgrading to new versions + * of the Elastic stack (via Upgrade Assistant) and will be surfaced in documentation + * created from HTTP API introspection (like OAS). + */ +export interface RouteDeprecationInfo { + documentationUrl: string; + severity: 'warning' | 'critical'; + reason: VersionBumpDeprecationType | RemovalApiDeprecationType | MigrationApiDeprecationType; +} + +/** + * bump deprecation reason denotes a new version of the API is available + */ +interface VersionBumpDeprecationType { + type: 'bump'; + newApiVersion: string; +} + +/** + * remove deprecation reason denotes the API was fully removed with no replacement + */ +interface RemovalApiDeprecationType { + type: 'remove'; +} + +/** + * migrate deprecation reason denotes the API has been migrated to a different API path + * Please make sure that if you are only incrementing the version of the API to use 'bump' instead + */ +interface MigrationApiDeprecationType { + type: 'migrate'; + newApiPath: string; + newApiMethod: string; +} + /** * A set of privileges that can be used to define complex authorization requirements. * @@ -187,6 +224,14 @@ export interface RouteSecurity { authc?: RouteAuthc; } +/** + * A set of reserved privileges that can be used to check access to the route. + */ +export enum ReservedPrivilegesSet { + operator = 'operator', + superuser = 'superuser', +} + /** * Additional route options. * @public @@ -277,12 +322,18 @@ export interface RouteConfigOptions { description?: string; /** - * Setting this to `true` declares this route to be deprecated. Consumers SHOULD - * refrain from usage of this route. + * Description of deprecations for this HTTP API. * - * @remarks This will be surfaced in OAS documentation. + * @remark This will assist Kibana HTTP API users when upgrading to new versions + * of the Elastic stack (via Upgrade Assistant) and will be surfaced in documentation + * created from HTTP API introspection (like OAS). + * + * Setting this object marks the route as deprecated. + * + * @remarks This may be surfaced in OAS documentation. + * @public */ - deprecated?: boolean; + deprecated?: RouteDeprecationInfo; /** * Whether this route should be treated as "invisible" and excluded from router @@ -321,6 +372,23 @@ export interface RouteConfigOptions { * @default false */ httpResource?: boolean; + + /** + * Based on the the ES API specification (see https://github.com/elastic/elasticsearch-specification) + * Kibana APIs can also specify some metadata about API availability. + * + * This setting is only applicable if your route `access` is `public`. + * + * @remark intended to be used for informational purposes only. + */ + availability?: { + /** @default stable */ + stability?: 'experimental' | 'beta' | 'stable'; + /** + * The stack version in which the route was introduced (eg: 8.15.0). + */ + since?: string; + }; } /** diff --git a/packages/core/http/core-http-server/src/router/router.ts b/packages/core/http/core-http-server/src/router/router.ts index ba2b5eb906a93..d8b79bee13025 100644 --- a/packages/core/http/core-http-server/src/router/router.ts +++ b/packages/core/http/core-http-server/src/router/router.ts @@ -10,7 +10,7 @@ import type { Request, ResponseObject, ResponseToolkit } from '@hapi/hapi'; import type Boom from '@hapi/boom'; import type { VersionedRouter } from '../versioning'; -import type { RouteConfig, RouteMethod } from './route'; +import type { RouteConfig, RouteDeprecationInfo, RouteMethod } from './route'; import type { RequestHandler, RequestHandlerWrapper } from './request_handler'; import type { RequestHandlerContextBase } from './request_handler_context'; import type { RouteConfigOptions } from './route'; @@ -98,7 +98,7 @@ export interface IRouter RouterRoute[]; + getRoutes: (options?: { excludeVersionedRoutes?: boolean }) => RouterRoute[]; /** * An instance very similar to {@link IRouter} that can be used for versioning HTTP routes @@ -139,4 +139,13 @@ export interface RouterRoute { req: Request, responseToolkit: ResponseToolkit ) => Promise>; + isVersioned: false; +} + +/** @public */ +export interface RouterDeprecatedRouteDetails { + routeDeprecationOptions: RouteDeprecationInfo; + routeMethod: RouteMethod; + routePath: string; + routeVersion?: string; } diff --git a/packages/core/http/core-http-server/src/versioning/index.ts b/packages/core/http/core-http-server/src/versioning/index.ts index 94b60bd105aac..8d8a664c769ac 100644 --- a/packages/core/http/core-http-server/src/versioning/index.ts +++ b/packages/core/http/core-http-server/src/versioning/index.ts @@ -19,4 +19,5 @@ export type { VersionedRouter, VersionedRouteCustomResponseBodyValidation, VersionedResponseBodyValidation, + VersionedRouterRoute, } from './types'; diff --git a/packages/core/http/core-http-server/src/versioning/types.ts b/packages/core/http/core-http-server/src/versioning/types.ts index 60cbca014e683..63e1e37754803 100644 --- a/packages/core/http/core-http-server/src/versioning/types.ts +++ b/packages/core/http/core-http-server/src/versioning/types.ts @@ -20,7 +20,7 @@ import type { RouteValidationFunction, LazyValidator, } from '../..'; - +import type { RouteDeprecationInfo } from '../router/route'; type RqCtx = RequestHandlerContextBase; export type { ApiVersion }; @@ -35,7 +35,7 @@ export type VersionedRouteConfig = Omit< > & { options?: Omit< RouteConfigOptions, - 'access' | 'description' | 'deprecated' | 'discontinued' | 'security' + 'access' | 'description' | 'summary' | 'deprecated' | 'discontinued' | 'security' >; /** See {@link RouteConfigOptions['access']} */ access: Exclude['access'], undefined>; @@ -89,17 +89,9 @@ export type VersionedRouteConfig = Omit< */ description?: string; - /** - * Declares this operation to be deprecated. Consumers SHOULD refrain from usage - * of this route. This will be surfaced in OAS documentation. - * - * @default false - */ - deprecated?: boolean; - /** * Release version or date that this route will be removed - * Use with `deprecated: true` + * Use with `deprecated: {@link RouteDeprecationInfo}` * * @default undefined */ @@ -234,6 +226,11 @@ export interface VersionedRouter { * @track-adoption */ delete: VersionedRouteRegistrar<'delete', Ctx>; + + /** + * @public + */ + getRoutes: () => VersionedRouterRoute[]; } /** @public */ @@ -341,6 +338,10 @@ export interface AddVersionOpts { validate: false | VersionedRouteValidation | (() => VersionedRouteValidation); // Provide a way to lazily load validation schemas security?: Exclude['security'], undefined>; + + options?: { + deprecated?: RouteDeprecationInfo; + }; } /** @@ -363,3 +364,11 @@ export interface VersionedRoute< handler: (...params: Parameters>) => MaybePromise ): VersionedRoute; } + +export interface VersionedRouterRoute

{ + method: string; + path: string; + options: Omit, 'path'>; + handlers: Array<{ fn: RequestHandler; options: AddVersionOpts }>; + isVersioned: true; +} diff --git a/packages/core/http/core-http-server/tsconfig.json b/packages/core/http/core-http-server/tsconfig.json index 64b2dacf2f292..50a0ce973eb4e 100644 --- a/packages/core/http/core-http-server/tsconfig.json +++ b/packages/core/http/core-http-server/tsconfig.json @@ -15,7 +15,7 @@ "@kbn/utility-types", "@kbn/core-base-common", "@kbn/core-http-common", - "@kbn/zod" + "@kbn/zod", ], "exclude": [ "target/**/*", diff --git a/packages/core/lifecycle/core-lifecycle-server-mocks/src/core_setup.mock.ts b/packages/core/lifecycle/core-lifecycle-server-mocks/src/core_setup.mock.ts index b50e9279d4721..30f5958bd92c5 100644 --- a/packages/core/lifecycle/core-lifecycle-server-mocks/src/core_setup.mock.ts +++ b/packages/core/lifecycle/core-lifecycle-server-mocks/src/core_setup.mock.ts @@ -76,6 +76,8 @@ export function createCoreSetupMock({ userProfile: userProfileServiceMock.createSetup(), coreUsageData: { registerUsageCounter: coreUsageDataServiceMock.createSetupContract().registerUsageCounter, + registerDeprecatedUsageFetch: + coreUsageDataServiceMock.createSetupContract().registerDeprecatedUsageFetch, }, plugins: { onSetup: jest.fn(), diff --git a/packages/core/plugins/core-plugins-server-internal/src/plugin_context.ts b/packages/core/plugins/core-plugins-server-internal/src/plugin_context.ts index 2fcdf384cb897..d7d40c9b792f7 100644 --- a/packages/core/plugins/core-plugins-server-internal/src/plugin_context.ts +++ b/packages/core/plugins/core-plugins-server-internal/src/plugin_context.ts @@ -225,6 +225,7 @@ export function createPluginSetupContext({ }, http: { createCookieSessionStorageFactory: deps.http.createCookieSessionStorageFactory, + getDeprecatedRoutes: deps.http.getDeprecatedRoutes, registerRouteHandlerContext: < Context extends RequestHandlerContext, ContextName extends keyof Omit @@ -283,6 +284,7 @@ export function createPluginSetupContext({ deprecations: deps.deprecations.getRegistry(plugin.name), coreUsageData: { registerUsageCounter: deps.coreUsageData.registerUsageCounter, + registerDeprecatedUsageFetch: deps.coreUsageData.registerDeprecatedUsageFetch, }, plugins: { onSetup: (...dependencyNames) => runtimeResolver.onSetup(plugin.name, dependencyNames), diff --git a/packages/core/root/core-root-server-internal/src/server.ts b/packages/core/root/core-root-server-internal/src/server.ts index 447db192c3048..5082a27930e87 100644 --- a/packages/core/root/core-root-server-internal/src/server.ts +++ b/packages/core/root/core-root-server-internal/src/server.ts @@ -276,10 +276,6 @@ export class Server { executionContext: executionContextSetup, }); - const deprecationsSetup = await this.deprecations.setup({ - http: httpSetup, - }); - // setup i18n prior to any other service, to have translations ready const i18nServiceSetup = await this.i18n.setup({ http: httpSetup, pluginPaths }); @@ -303,6 +299,11 @@ export class Server { changedDeprecatedConfigPath$: this.configService.getDeprecatedConfigPath$(), }); + const deprecationsSetup = await this.deprecations.setup({ + http: httpSetup, + coreUsageData: coreUsageDataSetup, + }); + const savedObjectsSetup = await this.savedObjects.setup({ http: httpSetup, elasticsearch: elasticsearchServiceSetup, diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/es_errors.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/es_errors.test.ts index 9a93f50487bcd..73a0fc0659939 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/es_errors.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/es_errors.test.ts @@ -8,6 +8,7 @@ */ import { + hasAllKeywordsInOrder, isClusterShardLimitExceeded, isIncompatibleMappingException, isIndexNotFoundException, @@ -128,3 +129,31 @@ describe('isClusterShardLimitExceeded', () => { expect(isClusterShardLimitExceeded(undefined)).toEqual(false); }); }); + +describe('hasAllKeywordsInOrder', () => { + it('returns false if not all keywords are present', () => { + expect( + hasAllKeywordsInOrder('some keywords in a message', ['some', 'in', 'message', 'missing']) + ).toEqual(false); + }); + + it('returns false if keywords are not in the right order', () => { + expect( + hasAllKeywordsInOrder('some keywords in a message', ['some', 'message', 'keywords']) + ).toEqual(false); + }); + + it('returns false if the message is empty', () => { + expect(hasAllKeywordsInOrder('', ['some', 'message', 'keywords'])).toEqual(false); + }); + + it('returns false if the keyword list is empty', () => { + expect(hasAllKeywordsInOrder('some keywords in a message', [])).toEqual(false); + }); + + it('returns true if keywords are present and in the right order', () => { + expect( + hasAllKeywordsInOrder('some keywords in a message', ['some', 'keywords', 'in', 'message']) + ).toEqual(true); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/es_errors.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/es_errors.ts index fbded8ad44b29..0ea6ccc227cba 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/es_errors.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/es_errors.ts @@ -7,16 +7,16 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { ErrorCause } from '@elastic/elasticsearch/lib/api/types'; -export const isWriteBlockException = (errorCause?: estypes.ErrorCause): boolean => { +export const isWriteBlockException = (errorCause?: ErrorCause): boolean => { return ( errorCause?.type === 'cluster_block_exception' && - errorCause?.reason?.match(/index \[.+] blocked by: \[FORBIDDEN\/8\/.+ \(api\)\]/) !== null + hasAllKeywordsInOrder(errorCause?.reason, ['index [', '] blocked by: [FORBIDDEN/8/', ' (api)]']) ); }; -export const isIncompatibleMappingException = (errorCause?: estypes.ErrorCause): boolean => { +export const isIncompatibleMappingException = (errorCause?: ErrorCause): boolean => { return ( errorCause?.type === 'strict_dynamic_mapping_exception' || errorCause?.type === 'mapper_parsing_exception' || @@ -24,17 +24,29 @@ export const isIncompatibleMappingException = (errorCause?: estypes.ErrorCause): ); }; -export const isIndexNotFoundException = (errorCause?: estypes.ErrorCause): boolean => { +export const isIndexNotFoundException = (errorCause?: ErrorCause): boolean => { return errorCause?.type === 'index_not_found_exception'; }; -export const isClusterShardLimitExceeded = (errorCause?: estypes.ErrorCause): boolean => { +export const isClusterShardLimitExceeded = (errorCause?: ErrorCause): boolean => { // traditional ES: validation_exception. serverless ES: illegal_argument_exception return ( (errorCause?.type === 'validation_exception' || errorCause?.type === 'illegal_argument_exception') && - errorCause?.reason?.match( - /this action would add .* shards, but this cluster currently has .* maximum normal shards open/ - ) !== null + hasAllKeywordsInOrder(errorCause?.reason, [ + 'this action would add', + 'shards, but this cluster currently has', + 'maximum normal shards open', + ]) ); }; + +export const hasAllKeywordsInOrder = (message: string | undefined, keywords: string[]): boolean => { + if (!message || !keywords.length) { + return false; + } + + const keywordIndices = keywords.map((keyword) => message?.indexOf(keyword) ?? -1); + // check that all keywords are present and in the right order + return keywordIndices.every((v, i, a) => v >= 0 && (!i || a[i - 1] <= v)); +}; diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/mocks/internal_mocks.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/mocks/internal_mocks.ts index 625c8ed77fb48..ff5fe86df1173 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/mocks/internal_mocks.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/mocks/internal_mocks.ts @@ -36,6 +36,7 @@ export const createCoreUsageDataSetupMock = () => { getClient: jest.fn(), registerUsageCounter: jest.fn(), incrementUsageCounter: jest.fn(), + registerDeprecatedUsageFetch: jest.fn(), }; return setupContract; }; diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_create.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_create.ts index b90aa0226d71c..c9a8656b3f753 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_create.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_create.ts @@ -38,6 +38,7 @@ export const registerBulkCreateRoute = ( summary: `Create saved objects`, tags: ['oas-tag:saved objects'], access, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, validate: { diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_delete.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_delete.ts index 0f33ddc384bed..65209a6072748 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_delete.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_delete.ts @@ -38,6 +38,7 @@ export const registerBulkDeleteRoute = ( summary: `Delete saved objects`, tags: ['oas-tag:saved objects'], access, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, validate: { diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_get.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_get.ts index 95fd9f5eab10a..3f87ca12248ae 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_get.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_get.ts @@ -38,6 +38,7 @@ export const registerBulkGetRoute = ( summary: `Get saved objects`, tags: ['oas-tag:saved objects'], access, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, validate: { diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_resolve.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_resolve.ts index d6b74131fb74d..8e19114e798e0 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_resolve.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_resolve.ts @@ -38,6 +38,7 @@ export const registerBulkResolveRoute = ( summary: `Resolve saved objects`, tags: ['oas-tag:saved objects'], access, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, description: `Retrieve multiple Kibana saved objects by ID, using any legacy URL aliases if they exist. Under certain circumstances, when Kibana is upgraded, saved object migrations may necessitate regenerating some object IDs to enable new features. When an object's ID is regenerated, a legacy URL alias is created for that object, preserving its old ID. In such a scenario, that object can be retrieved with the bulk resolve API using either its new ID or its old ID.`, diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_update.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_update.ts index 7a7ec340d98ca..825a5f95482c0 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_update.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_update.ts @@ -38,6 +38,7 @@ export const registerBulkUpdateRoute = ( summary: `Update saved objects`, tags: ['oas-tag:saved objects'], access, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, validate: { diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/create.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/create.ts index c8bfd4c0feaf9..57f4a10ed9377 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/create.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/create.ts @@ -38,6 +38,7 @@ export const registerCreateRoute = ( summary: `Create a saved object`, tags: ['oas-tag:saved objects'], access, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, validate: { diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/delete.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/delete.ts index 7ef8aac3fa1b1..69287821d8049 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/delete.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/delete.ts @@ -38,6 +38,7 @@ export const registerDeleteRoute = ( summary: `Delete a saved object`, tags: ['oas-tag:saved objects'], access, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, validate: { diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/find.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/find.ts index ac3b0555a7694..884ba1ed5c423 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/find.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/find.ts @@ -42,6 +42,7 @@ export const registerFindRoute = ( summary: `Search for saved objects`, tags: ['oas-tag:saved objects'], access, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, validate: { diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/get.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/get.ts index 9784ef1c79ff4..9fe3aa8ff20c7 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/get.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/get.ts @@ -38,6 +38,7 @@ export const registerGetRoute = ( summary: `Get a saved object`, tags: ['oas-tag:saved objects'], access, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, validate: { diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/resolve.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/resolve.ts index 295acacc0ba0e..28a6c82e9ffdf 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/resolve.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/resolve.ts @@ -34,6 +34,7 @@ export const registerResolveRoute = ( summary: `Resolve a saved object`, tags: ['oas-tag:saved objects'], access, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, description: `Retrieve a single Kibana saved object by ID, using any legacy URL alias if it exists. Under certain circumstances, when Kibana is upgraded, saved object migrations may necessitate regenerating some object IDs to enable new features. When an object's ID is regenerated, a legacy URL alias is created for that object, preserving its old ID. In such a scenario, that object can be retrieved with the resolve API using either its new ID or its old ID.`, diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/update.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/update.ts index 9eeea40a29b68..cfedc3ce03d2a 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/update.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/update.ts @@ -39,6 +39,7 @@ export const registerUpdateRoute = ( summary: `Update a saved object`, tags: ['oas-tag:saved objects'], access, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, validate: { diff --git a/packages/core/usage-data/core-usage-data-base-server-internal/src/usage_stats_client.ts b/packages/core/usage-data/core-usage-data-base-server-internal/src/usage_stats_client.ts index 649e972af2abc..044fb683fb69a 100644 --- a/packages/core/usage-data/core-usage-data-base-server-internal/src/usage_stats_client.ts +++ b/packages/core/usage-data/core-usage-data-base-server-internal/src/usage_stats_client.ts @@ -8,7 +8,7 @@ */ import type { KibanaRequest } from '@kbn/core-http-server'; -import type { CoreUsageStats } from '@kbn/core-usage-data-server'; +import type { CoreUsageStats, CoreDeprecatedApiUsageStats } from '@kbn/core-usage-data-server'; /** @internal */ export interface BaseIncrementOptions { @@ -38,6 +38,13 @@ export type IncrementSavedObjectsExportOptions = BaseIncrementOptions & { export interface ICoreUsageStatsClient { getUsageStats(): Promise; + getDeprecatedApiUsageStats(): Promise; + + incrementDeprecatedApi( + counterName: string, + options: { resolved?: boolean; incrementBy?: number } + ): Promise; + incrementSavedObjectsBulkCreate(options: BaseIncrementOptions): Promise; incrementSavedObjectsBulkGet(options: BaseIncrementOptions): Promise; diff --git a/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_data_service.ts b/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_data_service.ts index 2a10e06567d02..7ac7cbb7fbb57 100644 --- a/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_data_service.ts +++ b/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_data_service.ts @@ -37,6 +37,7 @@ import type { CoreConfigUsageData, CoreIncrementCounterParams, CoreUsageCounter, + DeprecatedApiUsageFetcher, } from '@kbn/core-usage-data-server'; import { CORE_USAGE_STATS_TYPE, @@ -48,6 +49,7 @@ import { type SavedObjectsServiceStart, } from '@kbn/core-saved-objects-server'; +import { ISavedObjectsRepository } from '@kbn/core-saved-objects-api-server'; import { isConfigured } from './is_configured'; import { coreUsageStatsType } from './saved_objects'; import { CoreUsageStatsClient } from './core_usage_stats_client'; @@ -88,6 +90,7 @@ export class CoreUsageDataService private coreUsageStatsClient?: CoreUsageStatsClient; private deprecatedConfigPaths: ChangedDeprecatedPaths = { set: [], unset: [] }; private incrementUsageCounter: CoreIncrementUsageCounter = () => {}; // Initially set to noop + private deprecatedApiUsageFetcher: DeprecatedApiUsageFetcher = async () => []; // Initially set to noop constructor(core: CoreContext) { this.logger = core.logger.get('core-usage-stats-service'); @@ -513,12 +516,21 @@ export class CoreUsageDataService } }; + const registerDeprecatedUsageFetch = (fetchFn: DeprecatedApiUsageFetcher) => { + this.deprecatedApiUsageFetcher = fetchFn; + }; + + const fetchDeprecatedUsageStats = (params: { soClient: ISavedObjectsRepository }) => { + return this.deprecatedApiUsageFetcher(params); + }; + this.coreUsageStatsClient = new CoreUsageStatsClient({ debugLogger: (message: string) => this.logger.debug(message), basePath: http.basePath, repositoryPromise: internalRepositoryPromise, stop$: this.stop$, incrementUsageCounter, + fetchDeprecatedUsageStats, }); const contract: InternalCoreUsageDataSetup = { @@ -526,6 +538,7 @@ export class CoreUsageDataService getClient: () => this.coreUsageStatsClient!, registerUsageCounter, incrementUsageCounter, + registerDeprecatedUsageFetch, }; return contract; diff --git a/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_stats_client.test.ts b/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_stats_client.test.ts index 948332c71f59a..9702e4b512345 100644 --- a/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_stats_client.test.ts +++ b/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_stats_client.test.ts @@ -52,6 +52,7 @@ describe('CoreUsageStatsClient', () => { debugLogger: debugLoggerMock, basePath: basePathMock, repositoryPromise: Promise.resolve(repositoryMock), + fetchDeprecatedUsageStats: jest.fn(), stop$, incrementUsageCounter: incrementUsageCounterMock, }); diff --git a/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_stats_client.ts b/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_stats_client.ts index 67ab6d9b30c9c..69eba9e1abf23 100644 --- a/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_stats_client.ts +++ b/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_stats_client.ts @@ -37,6 +37,7 @@ import { takeUntil, tap, } from 'rxjs'; +import type { DeprecatedApiUsageFetcher } from '@kbn/core-usage-data-server'; export const BULK_CREATE_STATS_PREFIX = 'apiCalls.savedObjectsBulkCreate'; export const BULK_GET_STATS_PREFIX = 'apiCalls.savedObjectsBulkGet'; @@ -108,6 +109,16 @@ export interface CoreUsageEvent { types?: string[]; } +/** + * Interface that models core events triggered by API deprecations. (e.g. SO HTTP API calls) + * @internal + */ +export interface CoreUsageDeprecatedApiEvent { + id: string; + resolved: boolean; + incrementBy: number; +} + /** @internal */ export interface CoreUsageStatsClientParams { debugLogger: (message: string) => void; @@ -116,6 +127,7 @@ export interface CoreUsageStatsClientParams { stop$: Observable; incrementUsageCounter: (params: CoreIncrementCounterParams) => void; bufferTimeMs?: number; + fetchDeprecatedUsageStats: DeprecatedApiUsageFetcher; } /** @internal */ @@ -126,6 +138,8 @@ export class CoreUsageStatsClient implements ICoreUsageStatsClient { private readonly fieldsToIncrement$ = new Subject(); private readonly flush$ = new Subject(); private readonly coreUsageEvents$ = new Subject(); + private readonly coreUsageDeprecatedApiCalls$ = new Subject(); + private readonly fetchDeprecatedUsageStats: DeprecatedApiUsageFetcher; constructor({ debugLogger, @@ -134,10 +148,12 @@ export class CoreUsageStatsClient implements ICoreUsageStatsClient { stop$, incrementUsageCounter, bufferTimeMs = DEFAULT_BUFFER_TIME_MS, + fetchDeprecatedUsageStats, }: CoreUsageStatsClientParams) { this.debugLogger = debugLogger; this.basePath = basePath; this.repositoryPromise = repositoryPromise; + this.fetchDeprecatedUsageStats = fetchDeprecatedUsageStats; this.fieldsToIncrement$ .pipe( takeUntil(stop$), @@ -180,6 +196,28 @@ export class CoreUsageStatsClient implements ICoreUsageStatsClient { ) .subscribe(); + this.coreUsageDeprecatedApiCalls$ + .pipe( + takeUntil(stop$), + tap(({ id, incrementBy, resolved }) => { + incrementUsageCounter({ + counterName: id, + counterType: `deprecated_api_call:${resolved ? 'resolved' : 'total'}`, + incrementBy, + }); + + if (resolved) { + // increment number of times the marked_as_resolve has been called + incrementUsageCounter({ + counterName: id, + counterType: 'deprecated_api_call:marked_as_resolved', + incrementBy: 1, + }); + } + }) + ) + .subscribe(); + this.coreUsageEvents$ .pipe( takeUntil(stop$), @@ -215,6 +253,20 @@ export class CoreUsageStatsClient implements ICoreUsageStatsClient { return coreUsageStats; } + public async incrementDeprecatedApi( + id: string, + { resolved = false, incrementBy = 1 }: { resolved: boolean; incrementBy: number } + ) { + const deprecatedField = resolved ? 'deprecated_api_calls_resolved' : 'deprecated_api_calls'; + this.coreUsageDeprecatedApiCalls$.next({ id, resolved, incrementBy }); + this.fieldsToIncrement$.next([`${deprecatedField}.total`]); + } + + public async getDeprecatedApiUsageStats() { + const repository = await this.repositoryPromise; + return await this.fetchDeprecatedUsageStats({ soClient: repository }); + } + public async incrementSavedObjectsBulkCreate(options: BaseIncrementOptions) { await this.updateUsageStats([], BULK_CREATE_STATS_PREFIX, options); } diff --git a/packages/core/usage-data/core-usage-data-server-mocks/src/core_usage_data_service.mock.ts b/packages/core/usage-data/core-usage-data-server-mocks/src/core_usage_data_service.mock.ts index c85aee50653d2..57628901c1a60 100644 --- a/packages/core/usage-data/core-usage-data-server-mocks/src/core_usage_data_service.mock.ts +++ b/packages/core/usage-data/core-usage-data-server-mocks/src/core_usage_data_service.mock.ts @@ -17,6 +17,7 @@ import { coreUsageStatsClientMock } from './core_usage_stats_client.mock'; const createSetupContractMock = (usageStatsClient = coreUsageStatsClientMock.create()) => { const setupContract: jest.Mocked = { registerType: jest.fn(), + registerDeprecatedUsageFetch: jest.fn(), getClient: jest.fn().mockReturnValue(usageStatsClient), registerUsageCounter: jest.fn(), incrementUsageCounter: jest.fn(), diff --git a/packages/core/usage-data/core-usage-data-server-mocks/src/core_usage_stats_client.mock.ts b/packages/core/usage-data/core-usage-data-server-mocks/src/core_usage_stats_client.mock.ts index f6b11204ca040..8896e9066ef78 100644 --- a/packages/core/usage-data/core-usage-data-server-mocks/src/core_usage_stats_client.mock.ts +++ b/packages/core/usage-data/core-usage-data-server-mocks/src/core_usage_stats_client.mock.ts @@ -12,6 +12,7 @@ import type { ICoreUsageStatsClient } from '@kbn/core-usage-data-base-server-int const createUsageStatsClientMock = () => ({ getUsageStats: jest.fn().mockResolvedValue({}), + getDeprecatedApiUsageStats: jest.fn().mockResolvedValue([]), incrementSavedObjectsBulkCreate: jest.fn().mockResolvedValue(null), incrementSavedObjectsBulkGet: jest.fn().mockResolvedValue(null), incrementSavedObjectsBulkResolve: jest.fn().mockResolvedValue(null), diff --git a/packages/core/usage-data/core-usage-data-server/index.ts b/packages/core/usage-data/core-usage-data-server/index.ts index 77fb0b1066750..45ed369ca8381 100644 --- a/packages/core/usage-data/core-usage-data-server/index.ts +++ b/packages/core/usage-data/core-usage-data-server/index.ts @@ -19,4 +19,6 @@ export type { CoreConfigUsageData, CoreServicesUsageData, CoreUsageStats, + CoreDeprecatedApiUsageStats, + DeprecatedApiUsageFetcher, } from './src'; diff --git a/packages/core/usage-data/core-usage-data-server/src/core_usage_stats.ts b/packages/core/usage-data/core-usage-data-server/src/core_usage_stats.ts index 36409a097129c..39df9d30d19c9 100644 --- a/packages/core/usage-data/core-usage-data-server/src/core_usage_stats.ts +++ b/packages/core/usage-data/core-usage-data-server/src/core_usage_stats.ts @@ -146,3 +146,16 @@ export interface CoreUsageStats { 'savedObjectsRepository.resolvedOutcome.notFound'?: number; 'savedObjectsRepository.resolvedOutcome.total'?: number; } + +/** + * @public + * + * CoreDeprecatedApiUsageStats are collected over time while Kibana is running. + */ +export interface CoreDeprecatedApiUsageStats { + apiId: string; + totalMarkedAsResolved: number; + markedAsResolvedLastCalledAt: string; + apiTotalCalls: number; + apiLastCalledAt: string; +} diff --git a/packages/core/usage-data/core-usage-data-server/src/index.ts b/packages/core/usage-data/core-usage-data-server/src/index.ts index 01cd52adbe986..05d0f02500053 100644 --- a/packages/core/usage-data/core-usage-data-server/src/index.ts +++ b/packages/core/usage-data/core-usage-data-server/src/index.ts @@ -12,11 +12,12 @@ export type { CoreEnvironmentUsageData, CoreConfigUsageData, } from './core_usage_data'; -export type { CoreUsageStats } from './core_usage_stats'; +export type { CoreUsageStats, CoreDeprecatedApiUsageStats } from './core_usage_stats'; export type { CoreUsageDataSetup, CoreUsageCounter, CoreIncrementUsageCounter, CoreIncrementCounterParams, + DeprecatedApiUsageFetcher, } from './setup_contract'; export type { CoreUsageData, ConfigUsageData, CoreUsageDataStart } from './start_contract'; diff --git a/packages/core/usage-data/core-usage-data-server/src/setup_contract.ts b/packages/core/usage-data/core-usage-data-server/src/setup_contract.ts index bd87563792e6d..30ed7edb6ce1d 100644 --- a/packages/core/usage-data/core-usage-data-server/src/setup_contract.ts +++ b/packages/core/usage-data/core-usage-data-server/src/setup_contract.ts @@ -7,12 +7,12 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import type { ISavedObjectsRepository } from '@kbn/core-saved-objects-api-server'; +import type { CoreDeprecatedApiUsageStats } from './core_usage_stats'; + /** * Internal API for registering the Usage Tracker used for Core's usage data payload. * - * @note This API should never be used to drive application logic and is only - * intended for telemetry purposes. - * * @public */ export interface CoreUsageDataSetup { @@ -21,6 +21,7 @@ export interface CoreUsageDataSetup { * when tracking events. */ registerUsageCounter: (usageCounter: CoreUsageCounter) => void; + registerDeprecatedUsageFetch: (fetchFn: DeprecatedApiUsageFetcher) => void; } /** @@ -49,3 +50,11 @@ export interface CoreIncrementCounterParams { * Method to call whenever an event occurs, so the counter can be increased. */ export type CoreIncrementUsageCounter = (params: CoreIncrementCounterParams) => void; + +/** + * @public + * Registers the deprecated API fetcher to be called to grab all the deprecated API usage details. + */ +export type DeprecatedApiUsageFetcher = (params: { + soClient: ISavedObjectsRepository; +}) => Promise; diff --git a/packages/core/usage-data/core-usage-data-server/tsconfig.json b/packages/core/usage-data/core-usage-data-server/tsconfig.json index 77d0aa6ade3b1..83abb04d150ac 100644 --- a/packages/core/usage-data/core-usage-data-server/tsconfig.json +++ b/packages/core/usage-data/core-usage-data-server/tsconfig.json @@ -12,5 +12,8 @@ ], "exclude": [ "target/**/*", + ], + "kbn_references": [ + "@kbn/core-saved-objects-api-server", ] } diff --git a/packages/kbn-apm-synthtrace-client/src/lib/entities/index.ts b/packages/kbn-apm-synthtrace-client/src/lib/entities/index.ts index 10cf982ff41ee..4242476735a11 100644 --- a/packages/kbn-apm-synthtrace-client/src/lib/entities/index.ts +++ b/packages/kbn-apm-synthtrace-client/src/lib/entities/index.ts @@ -11,8 +11,19 @@ import { Fields } from '../entity'; import { serviceEntity } from './service_entity'; import { hostEntity } from './host_entity'; import { containerEntity } from './container_entity'; +import { k8sClusterJobEntity } from './kubernetes/cluster_entity'; +import { k8sCronJobEntity } from './kubernetes/cron_job_entity'; +import { k8sDaemonSetEntity } from './kubernetes/daemon_set_entity'; +import { k8sDeploymentEntity } from './kubernetes/deployment_entity'; +import { k8sJobSetEntity } from './kubernetes/job_set_entity'; +import { k8sNodeEntity } from './kubernetes/node_entity'; +import { k8sPodEntity } from './kubernetes/pod_entity'; +import { k8sReplicaSetEntity } from './kubernetes/replica_set'; +import { k8sStatefulSetEntity } from './kubernetes/stateful_set'; +import { k8sContainerEntity } from './kubernetes/container_entity'; export type EntityDataStreamType = 'metrics' | 'logs' | 'traces'; +export type Schema = 'ecs' | 'semconv'; export type EntityFields = Fields & Partial<{ @@ -32,4 +43,20 @@ export type EntityFields = Fields & [key: string]: any; }>; -export const entities = { serviceEntity, hostEntity, containerEntity }; +export const entities = { + serviceEntity, + hostEntity, + containerEntity, + k8s: { + k8sClusterJobEntity, + k8sCronJobEntity, + k8sDaemonSetEntity, + k8sDeploymentEntity, + k8sJobSetEntity, + k8sNodeEntity, + k8sPodEntity, + k8sReplicaSetEntity, + k8sStatefulSetEntity, + k8sContainerEntity, + }, +}; diff --git a/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/cluster_entity.ts b/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/cluster_entity.ts new file mode 100644 index 0000000000000..9fa4c81d86ffb --- /dev/null +++ b/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/cluster_entity.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { Schema } from '..'; +import { K8sEntity } from '.'; + +export function k8sClusterJobEntity({ + schema, + name, + entityId, + ...others +}: { + schema: Schema; + name: string; + entityId: string; + [key: string]: any; +}) { + if (schema === 'ecs') { + return new K8sEntity(schema, { + 'entity.type': 'cluster', + 'orchestrator.cluster.name': name, + 'entity.id': entityId, + ...others, + }); + } + + return new K8sEntity(schema, { + 'entity.type': 'cluster', + 'k8s.cluster.uid': name, + 'entity.id': entityId, + ...others, + }); +} diff --git a/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/container_entity.ts b/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/container_entity.ts new file mode 100644 index 0000000000000..b05d412b0dd5c --- /dev/null +++ b/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/container_entity.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { Schema } from '..'; +import { K8sEntity } from '.'; + +export function k8sContainerEntity({ + schema, + id, + entityId, + ...others +}: { + schema: Schema; + id: string; + entityId: string; + [key: string]: any; +}) { + if (schema === 'ecs') { + return new K8sEntity(schema, { + 'entity.type': 'container', + 'kubernetes.container.id': id, + 'entity.id': entityId, + ...others, + }); + } + + return new K8sEntity(schema, { + 'entity.type': 'container', + 'container.id': id, + 'entity.id': entityId, + ...others, + }); +} diff --git a/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/cron_job_entity.ts b/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/cron_job_entity.ts new file mode 100644 index 0000000000000..8590378e699fb --- /dev/null +++ b/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/cron_job_entity.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { Schema } from '..'; +import { K8sEntity } from '.'; + +export function k8sCronJobEntity({ + schema, + name, + uid, + clusterName, + entityId, + ...others +}: { + schema: Schema; + name: string; + uid?: string; + clusterName?: string; + entityId: string; + [key: string]: any; +}) { + if (schema === 'ecs') { + return new K8sEntity(schema, { + 'entity.type': 'cron_job', + 'kubernetes.cronjob.name': name, + 'kubernetes.cronjob.uid': uid, + 'kubernetes.namespace': clusterName, + 'entity.id': entityId, + ...others, + }); + } + + return new K8sEntity(schema, { + 'entity.type': 'cron_job', + 'k8s.cronjob.name': name, + 'k8s.cronjob.uid': uid, + 'k8s.cluster.name': clusterName, + 'entity.id': entityId, + ...others, + }); +} diff --git a/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/daemon_set_entity.ts b/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/daemon_set_entity.ts new file mode 100644 index 0000000000000..7e20b1c6d506f --- /dev/null +++ b/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/daemon_set_entity.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { Schema } from '..'; +import { K8sEntity } from '.'; + +export function k8sDaemonSetEntity({ + schema, + name, + uid, + clusterName, + entityId, + ...others +}: { + schema: Schema; + name: string; + uid?: string; + clusterName?: string; + entityId: string; + [key: string]: any; +}) { + if (schema === 'ecs') { + return new K8sEntity(schema, { + 'entity.type': 'daemon_set', + 'kubernetes.daemonset.name': name, + 'kubernetes.daemonset.uid': uid, + 'kubernetes.namespace': clusterName, + 'entity.id': entityId, + ...others, + }); + } + + return new K8sEntity(schema, { + 'entity.type': 'daemon_set', + 'k8s.daemonset.name': name, + 'k8s.daemonset.uid': uid, + 'k8s.cluster.name': clusterName, + 'entity.id': entityId, + ...others, + }); +} diff --git a/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/deployment_entity.ts b/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/deployment_entity.ts new file mode 100644 index 0000000000000..7eabdd0827325 --- /dev/null +++ b/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/deployment_entity.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { Schema } from '..'; +import { K8sEntity } from '.'; + +export function k8sDeploymentEntity({ + schema, + name, + uid, + clusterName, + entityId, + ...others +}: { + schema: Schema; + name: string; + uid?: string; + clusterName?: string; + entityId: string; + [key: string]: any; +}) { + if (schema === 'ecs') { + return new K8sEntity(schema, { + 'entity.type': 'deployment', + 'kubernetes.deployment.name': name, + 'kubernetes.deployment.uid': uid, + 'kubernetes.namespace': clusterName, + 'entity.id': entityId, + ...others, + }); + } + + return new K8sEntity(schema, { + 'entity.type': 'deployment', + 'k8s.deployment.name': name, + 'k8s.deployment.uid': uid, + 'k8s.cluster.name': clusterName, + 'entity.id': entityId, + ...others, + }); +} diff --git a/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/index.ts b/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/index.ts new file mode 100644 index 0000000000000..36d7f8caf9601 --- /dev/null +++ b/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/index.ts @@ -0,0 +1,76 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import type { EntityFields, Schema } from '..'; +import { Serializable } from '../../serializable'; + +const identityFieldsMap: Record> = { + ecs: { + pod: ['kubernetes.pod.name'], + cluster: ['orchestrator.cluster.name'], + cron_job: ['kubernetes.cronjob.name'], + daemon_set: ['kubernetes.daemonset.name'], + deployment: ['kubernetes.deployment.name'], + job: ['kubernetes.job.name'], + node: ['kubernetes.node.name'], + replica_set: ['kubernetes.replicaset.name'], + stateful_set: ['kubernetes.statefulset.name'], + container: ['kubernetes.container.id'], + }, + semconv: { + pod: ['k8s.pod.name'], + cluster: ['k8s.cluster.uid'], + cron_job: ['k8s.cronjob.name'], + daemon_set: ['k8s.daemonset.name'], + deployment: ['k8s.deployment.name'], + job: ['k8s.job.name'], + node: ['k8s.node.uid'], + replica_set: ['k8s.replicaset.name'], + stateful_set: ['k8s.statefulset.name'], + container: ['container.id'], + }, +}; + +export class K8sEntity extends Serializable { + constructor(schema: Schema, fields: EntityFields) { + const entityType = fields['entity.type']; + if (entityType === undefined) { + throw new Error(`Entity type not defined: ${entityType}`); + } + + const entityTypeWithSchema = `kubernetes_${entityType}_${schema}`; + const identityFields = identityFieldsMap[schema][entityType]; + if (identityFields === undefined || identityFields.length === 0) { + throw new Error( + `Identity fields not defined for schema: ${schema} and entity type: ${entityType}` + ); + } + + super({ + ...fields, + 'entity.type': entityTypeWithSchema, + 'entity.definitionId': `builtin_${entityTypeWithSchema}`, + 'entity.identityFields': identityFields, + 'entity.displayName': getDisplayName({ identityFields, fields }), + }); + } +} + +function getDisplayName({ + identityFields, + fields, +}: { + identityFields: string[]; + fields: EntityFields; +}) { + return identityFields + .map((field) => fields[field]) + .filter((_) => _) + .join(':'); +} diff --git a/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/job_set_entity.ts b/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/job_set_entity.ts new file mode 100644 index 0000000000000..e0383563c7266 --- /dev/null +++ b/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/job_set_entity.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { Schema } from '..'; +import { K8sEntity } from '.'; + +export function k8sJobSetEntity({ + schema, + name, + uid, + clusterName, + entityId, + ...others +}: { + schema: Schema; + name: string; + uid?: string; + clusterName?: string; + entityId: string; + [key: string]: any; +}) { + if (schema === 'ecs') { + return new K8sEntity(schema, { + 'entity.type': 'job', + 'kubernetes.job.name': name, + 'kubernetes.job.uid': uid, + 'kubernetes.namespace': clusterName, + 'entity.id': entityId, + ...others, + }); + } + + return new K8sEntity(schema, { + 'entity.type': 'job', + 'k8s.job.name': name, + 'k8s.job.uid': uid, + 'k8s.cluster.name': clusterName, + 'entity.id': entityId, + ...others, + }); +} diff --git a/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/node_entity.ts b/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/node_entity.ts new file mode 100644 index 0000000000000..283df5250d41d --- /dev/null +++ b/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/node_entity.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { Schema } from '..'; +import { K8sEntity } from '.'; + +export function k8sNodeEntity({ + schema, + name, + uid, + clusterName, + entityId, + ...others +}: { + schema: Schema; + name: string; + uid?: string; + clusterName?: string; + entityId: string; + [key: string]: any; +}) { + if (schema === 'ecs') { + return new K8sEntity(schema, { + 'entity.type': 'node', + 'kubernetes.node.name': name, + 'kubernetes.node.uid': uid, + 'kubernetes.namespace': clusterName, + 'entity.id': entityId, + ...others, + }); + } + + return new K8sEntity(schema, { + 'entity.type': 'node', + 'k8s.node.uid': uid, + 'k8s.cluster.name': clusterName, + 'entity.id': entityId, + ...others, + }); +} diff --git a/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/pod_entity.ts b/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/pod_entity.ts new file mode 100644 index 0000000000000..1b71c4e39a4fc --- /dev/null +++ b/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/pod_entity.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { Schema } from '..'; +import { K8sEntity } from '.'; + +export function k8sPodEntity({ + schema, + name, + uid, + clusterName, + entityId, + ...others +}: { + schema: Schema; + name: string; + uid?: string; + clusterName?: string; + entityId: string; + [key: string]: any; +}) { + if (schema === 'ecs') { + return new K8sEntity(schema, { + 'entity.type': 'pod', + 'kubernetes.pod.name': name, + 'kubernetes.pod.uid': uid, + 'kubernetes.namespace': clusterName, + 'entity.id': entityId, + ...others, + }); + } + + return new K8sEntity(schema, { + 'entity.type': 'pod', + 'k8s.pod.name': name, + 'k8s.pod.uid': uid, + 'k8s.cluster.name': clusterName, + 'entity.id': entityId, + ...others, + }); +} diff --git a/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/replica_set.ts b/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/replica_set.ts new file mode 100644 index 0000000000000..fcf20c0530c30 --- /dev/null +++ b/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/replica_set.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { Schema } from '..'; +import { K8sEntity } from '.'; + +export function k8sReplicaSetEntity({ + schema, + name, + uid, + clusterName, + entityId, + ...others +}: { + schema: Schema; + name: string; + uid?: string; + clusterName?: string; + entityId: string; + [key: string]: any; +}) { + if (schema === 'ecs') { + return new K8sEntity(schema, { + 'entity.type': 'replica_set', + 'kubernetes.replicaset.name': name, + 'kubernetes.replicaset.uid': uid, + 'kubernetes.namespace': clusterName, + 'entity.id': entityId, + ...others, + }); + } + + return new K8sEntity(schema, { + 'entity.type': 'replica_set', + 'k8s.replicaset.name': name, + 'k8s.replicaset.uid': uid, + 'k8s.cluster.name': clusterName, + 'entity.id': entityId, + ...others, + }); +} diff --git a/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/stateful_set.ts b/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/stateful_set.ts new file mode 100644 index 0000000000000..58c603704ebc0 --- /dev/null +++ b/packages/kbn-apm-synthtrace-client/src/lib/entities/kubernetes/stateful_set.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { Schema } from '..'; +import { K8sEntity } from '.'; + +export function k8sStatefulSetEntity({ + schema, + name, + uid, + clusterName, + entityId, + ...others +}: { + schema: Schema; + name: string; + uid?: string; + clusterName?: string; + entityId: string; + [key: string]: any; +}) { + if (schema === 'ecs') { + return new K8sEntity(schema, { + 'entity.type': 'stateful_set', + 'kubernetes.statefulset.name': name, + 'kubernetes.statefulset.uid': uid, + 'kubernetes.namespace': clusterName, + 'entity.id': entityId, + ...others, + }); + } + + return new K8sEntity(schema, { + 'entity.type': 'stateful_set', + 'k8s.statefulset.name': name, + 'k8s.statefulset.uid': uid, + 'k8s.cluster.name': clusterName, + 'entity.id': entityId, + ...others, + }); +} diff --git a/packages/kbn-apm-synthtrace/src/lib/entities/entities_synthtrace_es_client.ts b/packages/kbn-apm-synthtrace/src/lib/entities/entities_synthtrace_es_client.ts index 684e3efc0f372..65bf290eae32f 100644 --- a/packages/kbn-apm-synthtrace/src/lib/entities/entities_synthtrace_es_client.ts +++ b/packages/kbn-apm-synthtrace/src/lib/entities/entities_synthtrace_es_client.ts @@ -82,7 +82,8 @@ function getRoutingTransform() { const entityIndexName = `${entityType}s`; document._action = { index: { - _index: `.entities.v1.latest.builtin_${entityIndexName}_from_ecs_data`, + _index: + `.entities.v1.latest.builtin_${entityIndexName}_from_ecs_data`.toLocaleLowerCase(), _id: document['entity.id'], }, }; diff --git a/packages/kbn-apm-synthtrace/src/scenarios/k8s_entities.ts b/packages/kbn-apm-synthtrace/src/scenarios/k8s_entities.ts new file mode 100644 index 0000000000000..7d94cc3180a7e --- /dev/null +++ b/packages/kbn-apm-synthtrace/src/scenarios/k8s_entities.ts @@ -0,0 +1,155 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { EntityFields, entities, generateShortId } from '@kbn/apm-synthtrace-client'; +import { Schema } from '@kbn/apm-synthtrace-client/src/lib/entities'; +import { Scenario } from '../cli/scenario'; +import { withClient } from '../lib/utils/with_client'; + +const CLUSTER_NAME = 'cluster_foo'; + +const CLUSTER_ENTITY_ID = generateShortId(); +const POD_ENTITY_ID = generateShortId(); +const POD_UID = generateShortId(); +const REPLICA_SET_ENTITY_ID = generateShortId(); +const REPLICA_SET_UID = generateShortId(); +const DEPLOYMENT_ENTITY_ID = generateShortId(); +const DEPLOYMENT_UID = generateShortId(); +const STATEFUL_SET_ENTITY_ID = generateShortId(); +const STATEFUL_SET_UID = generateShortId(); +const DAEMON_SET_ENTITY_ID = generateShortId(); +const DAEMON_SET_UID = generateShortId(); +const JOB_SET_ENTITY_ID = generateShortId(); +const JOB_SET_UID = generateShortId(); +const CRON_JOB_ENTITY_ID = generateShortId(); +const CRON_JOB_UID = generateShortId(); +const NODE_ENTITY_ID = generateShortId(); +const NODE_UID = generateShortId(); + +const scenario: Scenario> = async (runOptions) => { + const { logger } = runOptions; + + return { + bootstrap: async ({ entitiesKibanaClient }) => { + await entitiesKibanaClient.installEntityIndexPatterns(); + }, + generate: ({ range, clients: { entitiesEsClient } }) => { + const getK8sEntitiesEvents = (schema: Schema) => + range + .interval('1m') + .rate(1) + .generator((timestamp) => { + return [ + entities.k8s + .k8sClusterJobEntity({ + schema, + name: CLUSTER_NAME, + entityId: CLUSTER_ENTITY_ID, + }) + .timestamp(timestamp), + entities.k8s + .k8sPodEntity({ + schema, + clusterName: CLUSTER_NAME, + name: 'pod_foo', + uid: POD_UID, + entityId: POD_ENTITY_ID, + }) + .timestamp(timestamp), + entities.k8s + .k8sReplicaSetEntity({ + clusterName: CLUSTER_NAME, + name: 'replica_set_foo', + schema, + uid: REPLICA_SET_UID, + entityId: REPLICA_SET_ENTITY_ID, + }) + .timestamp(timestamp), + entities.k8s + .k8sDeploymentEntity({ + clusterName: CLUSTER_NAME, + name: 'deployment_foo', + schema, + uid: DEPLOYMENT_UID, + entityId: DEPLOYMENT_ENTITY_ID, + }) + .timestamp(timestamp), + entities.k8s + .k8sStatefulSetEntity({ + clusterName: CLUSTER_NAME, + name: 'stateful_set_foo', + schema, + uid: STATEFUL_SET_UID, + entityId: STATEFUL_SET_ENTITY_ID, + }) + .timestamp(timestamp), + entities.k8s + .k8sDaemonSetEntity({ + clusterName: CLUSTER_NAME, + name: 'daemon_set_foo', + schema, + uid: DAEMON_SET_UID, + entityId: DAEMON_SET_ENTITY_ID, + }) + .timestamp(timestamp), + entities.k8s + .k8sJobSetEntity({ + clusterName: CLUSTER_NAME, + name: 'job_set_foo', + schema, + uid: JOB_SET_UID, + entityId: JOB_SET_ENTITY_ID, + }) + .timestamp(timestamp), + entities.k8s + .k8sCronJobEntity({ + clusterName: CLUSTER_NAME, + name: 'cron_job_foo', + schema, + uid: CRON_JOB_UID, + entityId: CRON_JOB_ENTITY_ID, + }) + .timestamp(timestamp), + entities.k8s + .k8sNodeEntity({ + clusterName: CLUSTER_NAME, + name: 'node_job_foo', + schema, + uid: NODE_UID, + entityId: NODE_ENTITY_ID, + }) + .timestamp(timestamp), + entities.k8s + .k8sContainerEntity({ + id: '123', + schema, + entityId: NODE_ENTITY_ID, + }) + .timestamp(timestamp), + ]; + }); + + const ecsEntities = getK8sEntitiesEvents('ecs'); + const otelEntities = getK8sEntitiesEvents('semconv'); + + return [ + withClient( + entitiesEsClient, + logger.perf('generating_entities_ecs_events', () => ecsEntities) + ), + withClient( + entitiesEsClient, + logger.perf('generating_entities_otel_events', () => otelEntities) + ), + ]; + }, + }; +}; + +export default scenario; diff --git a/packages/kbn-dev-utils/src/plugin_list/run_plugin_list_cli.ts b/packages/kbn-dev-utils/src/plugin_list/run_plugin_list_cli.ts index a201cfcd0e262..65f6735e22ca6 100644 --- a/packages/kbn-dev-utils/src/plugin_list/run_plugin_list_cli.ts +++ b/packages/kbn-dev-utils/src/plugin_list/run_plugin_list_cli.ts @@ -20,14 +20,39 @@ const OUTPUT_PATH = Path.resolve(REPO_ROOT, 'docs/developer/plugin-list.asciidoc export function runPluginListCli() { run(async ({ log }) => { log.info('looking for oss plugins'); - const ossPlugins = discoverPlugins('src/plugins'); - log.success(`found ${ossPlugins.length} plugins`); + const ossLegacyPlugins = discoverPlugins('src/plugins'); + const ossPlatformPlugins = discoverPlugins('src/platform/plugins'); + log.success(`found ${ossLegacyPlugins.length + ossPlatformPlugins.length} plugins`); log.info('looking for x-pack plugins'); - const xpackPlugins = discoverPlugins('x-pack/plugins'); - log.success(`found ${xpackPlugins.length} plugins`); + const xpackLegacyPlugins = discoverPlugins('x-pack/plugins'); + const xpackPlatformPlugins = discoverPlugins('x-pack/platform/plugins'); + const xpackSearchPlugins = discoverPlugins('x-pack/solutions/search/plugins'); + const xpackSecurityPlugins = discoverPlugins('x-pack/solutions/security/plugins'); + const xpackObservabilityPlugins = discoverPlugins('x-pack/solutions/observability/plugins'); + log.success( + `found ${ + xpackLegacyPlugins.length + + xpackPlatformPlugins.length + + xpackSearchPlugins.length + + xpackSecurityPlugins.length + + xpackObservabilityPlugins.length + } plugins` + ); log.info('writing plugin list to', OUTPUT_PATH); - Fs.writeFileSync(OUTPUT_PATH, generatePluginList(ossPlugins, xpackPlugins)); + Fs.writeFileSync( + OUTPUT_PATH, + generatePluginList( + [...ossLegacyPlugins, ...ossPlatformPlugins], + [ + ...xpackLegacyPlugins, + ...xpackPlatformPlugins, + ...xpackSearchPlugins, + ...xpackSecurityPlugins, + ...xpackObservabilityPlugins, + ] + ) + ); }); } diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index 6cbf6107b770a..2e9183ed18b56 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -578,7 +578,7 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D alertingRules: `${ELASTICSEARCH_DOCS}transform-alerts.html`, }, visualize: { - guide: `${KIBANA_DOCS}dashboard.html`, + guide: `${KIBANA_DOCS}_panels_and_visualizations.html`, lens: `${ELASTIC_WEBSITE_URL}what-is/kibana-lens`, lensPanels: `${KIBANA_DOCS}lens.html`, maps: `${ELASTIC_WEBSITE_URL}maps`, diff --git a/packages/kbn-eslint-config/.eslintrc.js b/packages/kbn-eslint-config/.eslintrc.js index f241131cd6273..ec39d88606438 100644 --- a/packages/kbn-eslint-config/.eslintrc.js +++ b/packages/kbn-eslint-config/.eslintrc.js @@ -317,6 +317,7 @@ module.exports = { '@kbn/disable/no_naked_eslint_disable': 'error', '@kbn/eslint/no_async_promise_body': 'error', '@kbn/eslint/no_async_foreach': 'error', + '@kbn/eslint/no_deprecated_authz_config': 'error', '@kbn/eslint/no_trailing_import_slash': 'error', '@kbn/eslint/no_constructor_args_in_property_initializers': 'error', '@kbn/eslint/no_this_in_property_initializers': 'error', @@ -326,7 +327,8 @@ module.exports = { '@kbn/imports/uniform_imports': 'error', '@kbn/imports/no_unused_imports': 'error', '@kbn/imports/no_boundary_crossing': 'error', - + '@kbn/imports/no_group_crossing_manifests': 'error', + '@kbn/imports/no_group_crossing_imports': 'error', 'no-new-func': 'error', 'no-implied-eval': 'error', 'no-prototype-builtins': 'error', diff --git a/packages/kbn-eslint-plugin-disable/src/helpers/protected_rules.ts b/packages/kbn-eslint-plugin-disable/src/helpers/protected_rules.ts index 0eabafc48ab69..6e555f1d9527c 100644 --- a/packages/kbn-eslint-plugin-disable/src/helpers/protected_rules.ts +++ b/packages/kbn-eslint-plugin-disable/src/helpers/protected_rules.ts @@ -12,4 +12,6 @@ export const PROTECTED_RULES = new Set([ '@kbn/disable/no_protected_eslint_disable', '@kbn/disable/no_naked_eslint_disable', '@kbn/imports/no_unused_imports', + '@kbn/imports/no_group_crossing_imports', + '@kbn/imports/no_group_crossing_manifests', ]); diff --git a/packages/kbn-eslint-plugin-eslint/rules/no_deprecated_authz_config.js b/packages/kbn-eslint-plugin-eslint/rules/no_deprecated_authz_config.js index f6485d0914c15..0f0b8759b4a82 100644 --- a/packages/kbn-eslint-plugin-eslint/rules/no_deprecated_authz_config.js +++ b/packages/kbn-eslint-plugin-eslint/rules/no_deprecated_authz_config.js @@ -39,6 +39,33 @@ const maybeReportDisabledSecurityConfig = (node, context, isVersionedRoute = fal return; } + const hasSecurityInRoot = (config) => { + const securityInRoot = config.properties.find( + (property) => property.key && property.key.name === 'security' + ); + + if (securityInRoot) { + return true; + } + + const optionsProperty = config.properties.find( + (prop) => prop.key && prop.key.name === 'options' + ); + + if (optionsProperty?.value?.properties) { + const tagsProperty = optionsProperty.value.properties.find( + (prop) => prop.key.name === 'tags' + ); + + const accessTagsFilter = (el) => isLiteralAccessTag(el) || isTemplateLiteralAccessTag(el); + const accessTags = tagsProperty?.value?.elements?.filter(accessTagsFilter) ?? []; + + return accessTags.length > 0; + } + + return false; + }; + if (isVersionedRoute) { const [versionConfig] = node.arguments; @@ -53,33 +80,6 @@ const maybeReportDisabledSecurityConfig = (node, context, isVersionedRoute = fal let currentNode = node; - const hasSecurityInRoot = (config) => { - const securityInRoot = config.properties.find( - (property) => property.key && property.key.name === 'security' - ); - - if (securityInRoot) { - return true; - } - - const optionsProperty = config.properties.find( - (prop) => prop.key && prop.key.name === 'options' - ); - - if (optionsProperty?.value?.properties) { - const tagsProperty = optionsProperty.value.properties.find( - (prop) => prop.key.name === 'tags' - ); - - const accessTagsFilter = (el) => isLiteralAccessTag(el) || isTemplateLiteralAccessTag(el); - const accessTags = tagsProperty.value.elements.filter(accessTagsFilter); - - return accessTags.length > 0; - } - - return false; - }; - while ( currentNode && currentNode.type === 'CallExpression' && @@ -126,11 +126,14 @@ const maybeReportDisabledSecurityConfig = (node, context, isVersionedRoute = fal } } else { const [routeConfig] = node.arguments; - const securityProperty = routeConfig.properties.find( - (property) => property.key && property.key.name === 'security' - ); - if (!securityProperty) { + const pathProperty = routeConfig.properties?.find((prop) => prop?.key?.name === 'path'); + + if (!pathProperty) { + return; + } + + if (!hasSecurityInRoot(routeConfig)) { const pathProperty = routeConfig.properties.find((prop) => prop.key.name === 'path'); context.report({ node: routeConfig, @@ -181,7 +184,14 @@ const handleRouteConfig = (node, context, isVersionedRoute = false) => { const staticPart = firstQuasi.split(ACCESS_TAG_PREFIX)[1] || ''; const dynamicParts = el.expressions.map((expression, index) => { - const dynamicPlaceholder = `\${${expression.name}}`; + let dynamicPlaceholder; + if (expression.property) { + // Case: object.property + dynamicPlaceholder = `\${${expression.object.name}.${expression.property.name}}`; + } else { + // Case: simple variable + dynamicPlaceholder = `\${${expression.name}}`; + } const nextQuasi = el.quasis[index + 1].value.raw || ''; return `${dynamicPlaceholder}${nextQuasi}`; }); @@ -290,13 +300,25 @@ module.exports = { CallExpression(node) { const callee = node.callee; + // Skipping by default if any of env vars is not set + const shouldSkipMigration = + !process.env.MIGRATE_ENABLED_AUTHZ && !process.env.MIGRATE_DISABLED_AUTHZ; + + if (shouldSkipMigration) { + return; + } + if ( callee.type === 'MemberExpression' && callee.object && callee.object.name === 'router' && routeMethods.includes(callee.property.name) ) { - handleRouteConfig(node, context, false); + if (process.env.MIGRATE_ENABLED_AUTHZ === 'false') { + maybeReportDisabledSecurityConfig(node, context, false); + } else { + handleRouteConfig(node, context, false); + } } if ( @@ -310,7 +332,11 @@ module.exports = { const versionConfig = node.arguments[0]; if (versionConfig && versionConfig.type === 'ObjectExpression') { - handleRouteConfig(node, context, true); + if (process.env.MIGRATE_ENABLED_AUTHZ === 'false') { + maybeReportDisabledSecurityConfig(node, context, true); + } else { + handleRouteConfig(node, context, true); + } } } }, diff --git a/packages/kbn-eslint-plugin-eslint/rules/no_deprecated_authz_config.test.js b/packages/kbn-eslint-plugin-eslint/rules/no_deprecated_authz_config.test.js index b397c4457b2c7..4f0fc375ad55e 100644 --- a/packages/kbn-eslint-plugin-eslint/rules/no_deprecated_authz_config.test.js +++ b/packages/kbn-eslint-plugin-eslint/rules/no_deprecated_authz_config.test.js @@ -11,8 +11,17 @@ const { RuleTester } = require('eslint'); const rule = require('./no_deprecated_authz_config'); const dedent = require('dedent'); -// Indentation is a big problem in the test cases, dedent library does not work as expected. +beforeAll(() => { + process.env.MIGRATE_ENABLED_AUTHZ = 'true'; + process.env.MIGRATE_DISABLED_AUTHZ = 'true'; +}); + +afterAll(() => { + delete process.env.MIGRATE_ENABLED_AUTHZ; + delete process.env.MIGRATE_DISABLED_AUTHZ; +}); +// Indentation is a big problem in the test cases, dedent library does not work as expected. const ruleTester = new RuleTester({ parser: require.resolve('@typescript-eslint/parser'), parserOptions: { @@ -202,6 +211,28 @@ ruleTester.run('no_deprecated_authz_config', rule, { `, name: 'invalid: access tags are template literals, move to security.authz.requiredPrivileges', }, + { + code: ` + router.get({ + path: '/some/path', + options: { + tags: [\`access:\${APP.TEST_ID}\`], + }, + }); + `, + errors: [{ message: "Move 'access' tags to security.authz.requiredPrivileges." }], + output: ` + router.get({ + path: '/some/path', + security: { + authz: { + requiredPrivileges: [\`\${APP.TEST_ID}\`], + }, + }, + }); + `, + name: 'invalid: access tags are template literals, move to security.authz.requiredPrivileges', + }, { code: ` router.get({ diff --git a/packages/kbn-eslint-plugin-imports/index.ts b/packages/kbn-eslint-plugin-imports/index.ts index 9c57d66f60225..31e3483ea6139 100644 --- a/packages/kbn-eslint-plugin-imports/index.ts +++ b/packages/kbn-eslint-plugin-imports/index.ts @@ -13,6 +13,8 @@ import { UniformImportsRule } from './src/rules/uniform_imports'; import { ExportsMovedPackagesRule } from './src/rules/exports_moved_packages'; import { NoUnusedImportsRule } from './src/rules/no_unused_imports'; import { NoBoundaryCrossingRule } from './src/rules/no_boundary_crossing'; +import { NoGroupCrossingImportsRule } from './src/rules/no_group_crossing_imports'; +import { NoGroupCrossingManifestsRule } from './src/rules/no_group_crossing_manifests'; import { RequireImportRule } from './src/rules/require_import'; /** @@ -25,5 +27,7 @@ export const rules = { exports_moved_packages: ExportsMovedPackagesRule, no_unused_imports: NoUnusedImportsRule, no_boundary_crossing: NoBoundaryCrossingRule, + no_group_crossing_imports: NoGroupCrossingImportsRule, + no_group_crossing_manifests: NoGroupCrossingManifestsRule, require_import: RequireImportRule, }; diff --git a/packages/kbn-eslint-plugin-imports/src/helpers/groups.ts b/packages/kbn-eslint-plugin-imports/src/helpers/groups.ts new file mode 100644 index 0000000000000..a76251f028389 --- /dev/null +++ b/packages/kbn-eslint-plugin-imports/src/helpers/groups.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import type { ModuleGroup, ModuleVisibility } from '@kbn/repo-info/types'; + +/** + * Checks whether a given ModuleGroup can import from another one + * @param importerGroup The group of the module that we are checking + * @param importedGroup The group of the imported module + * @param importedVisibility The visibility of the imported module + * @returns true if importerGroup is allowed to import from importedGroup/Visibiliy + */ +export function isImportableFrom( + importerGroup: ModuleGroup, + importedGroup: ModuleGroup, + importedVisibility: ModuleVisibility +): boolean { + return importerGroup === importedGroup || importedVisibility === 'shared'; +} diff --git a/packages/kbn-eslint-plugin-imports/src/helpers/report.ts b/packages/kbn-eslint-plugin-imports/src/helpers/report.ts index 9ac0171507efd..11fc09fbecab3 100644 --- a/packages/kbn-eslint-plugin-imports/src/helpers/report.ts +++ b/packages/kbn-eslint-plugin-imports/src/helpers/report.ts @@ -30,3 +30,19 @@ export function report(context: Rule.RuleContext, options: ReportOptions) { : null, }); } + +export const toList = (strings: string[]) => { + const items = strings.map((s) => `"${s}"`); + const list = items.slice(0, -1).join(', '); + const last = items.at(-1); + return !list.length ? last ?? '' : `${list} or ${last}`; +}; + +export const formatSuggestions = (suggestions: string[]) => { + const s = suggestions.map((l) => l.trim()).filter(Boolean); + if (!s.length) { + return ''; + } + + return ` \nSuggestions:\n - ${s.join('\n - ')}\n\n`; +}; diff --git a/packages/kbn-eslint-plugin-imports/src/rules/no_boundary_crossing.test.ts b/packages/kbn-eslint-plugin-imports/src/rules/no_boundary_crossing.test.ts index be9e60978fa88..f44c0571b2c94 100644 --- a/packages/kbn-eslint-plugin-imports/src/rules/no_boundary_crossing.test.ts +++ b/packages/kbn-eslint-plugin-imports/src/rules/no_boundary_crossing.test.ts @@ -9,8 +9,9 @@ import { RuleTester } from 'eslint'; import { NoBoundaryCrossingRule } from './no_boundary_crossing'; -import { ModuleType } from '@kbn/repo-source-classifier'; +import type { ModuleType } from '@kbn/repo-source-classifier'; import dedent from 'dedent'; +import { formatSuggestions } from '../helpers/report'; const make = (from: ModuleType, to: ModuleType, imp = 'import') => ({ filename: `${from}.ts`, @@ -107,13 +108,12 @@ for (const [name, tester] of [tsTester, babelTester]) { data: { importedType: 'server package', ownType: 'common package', - suggestion: ` ${dedent` - Suggestions: - - Remove the import statement. - - Limit your imports to "common package" or "static" code. - - Covert to a type-only import. - - Reach out to #kibana-operations for help. - `}`, + suggestion: formatSuggestions([ + 'Remove the import statement.', + 'Limit your imports to "common package" or "static" code.', + 'Covert to a type-only import.', + 'Reach out to #kibana-operations for help.', + ]), }, }, ], diff --git a/packages/kbn-eslint-plugin-imports/src/rules/no_boundary_crossing.ts b/packages/kbn-eslint-plugin-imports/src/rules/no_boundary_crossing.ts index 59c73c1d0336c..3f426e13a6215 100644 --- a/packages/kbn-eslint-plugin-imports/src/rules/no_boundary_crossing.ts +++ b/packages/kbn-eslint-plugin-imports/src/rules/no_boundary_crossing.ts @@ -12,13 +12,14 @@ import Path from 'path'; import { TSESTree } from '@typescript-eslint/typescript-estree'; import * as Bt from '@babel/types'; import type { Rule } from 'eslint'; -import ESTree from 'estree'; -import { ModuleType } from '@kbn/repo-source-classifier'; +import type { Node } from 'estree'; +import type { ModuleType } from '@kbn/repo-source-classifier'; import { visitAllImportStatements, Importer } from '../helpers/visit_all_import_statements'; import { getSourcePath } from '../helpers/source'; import { getRepoSourceClassifier } from '../helpers/repo_source_classifier'; import { getImportResolver } from '../get_import_resolver'; +import { formatSuggestions, toList } from '../helpers/report'; const ANY = Symbol(); @@ -33,22 +34,6 @@ const IMPORTABLE_FROM: Record = { tooling: ANY, }; -const toList = (strings: string[]) => { - const items = strings.map((s) => `"${s}"`); - const list = items.slice(0, -1).join(', '); - const last = items.at(-1); - return !list.length ? last ?? '' : `${list} or ${last}`; -}; - -const formatSuggestions = (suggestions: string[]) => { - const s = suggestions.map((l) => l.trim()).filter(Boolean); - if (!s.length) { - return ''; - } - - return ` Suggestions:\n - ${s.join('\n - ')}`; -}; - const isTypeOnlyImport = (importer: Importer) => { // handle babel nodes if (Bt.isImportDeclaration(importer)) { @@ -125,7 +110,7 @@ export const NoBoundaryCrossingRule: Rule.RuleModule = { if (!importable.includes(imported.type)) { context.report({ - node: node as ESTree.Node, + node: node as Node, messageId: 'TYPE_MISMATCH', data: { ownType: self.type, diff --git a/packages/kbn-eslint-plugin-imports/src/rules/no_group_crossing_imports.test.ts b/packages/kbn-eslint-plugin-imports/src/rules/no_group_crossing_imports.test.ts new file mode 100644 index 0000000000000..dc4828603f73f --- /dev/null +++ b/packages/kbn-eslint-plugin-imports/src/rules/no_group_crossing_imports.test.ts @@ -0,0 +1,155 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { RuleTester } from 'eslint'; +import dedent from 'dedent'; +import { NoGroupCrossingImportsRule } from './no_group_crossing_imports'; +import { formatSuggestions } from '../helpers/report'; +import { ModuleGroup, ModuleVisibility } from '@kbn/repo-info/types'; + +const make = ( + fromGroup: ModuleGroup, + fromVisibility: ModuleVisibility, + toGroup: ModuleGroup, + toVisibility: ModuleVisibility, + imp = 'import' +) => ({ + filename: `${fromGroup}.${fromVisibility}.ts`, + code: dedent` + ${imp} '${toGroup}.${toVisibility}' + `, +}); + +jest.mock('../get_import_resolver', () => { + return { + getImportResolver() { + return { + resolve(req: string) { + return { + type: 'file', + absolute: req.split('.'), + }; + }, + }; + }, + }; +}); + +jest.mock('../helpers/repo_source_classifier', () => { + return { + getRepoSourceClassifier() { + return { + classify(r: string | [string, string]) { + const [group, visibility] = + typeof r === 'string' ? (r.endsWith('.ts') ? r.slice(0, -3) : r).split('.') : r; + return { + pkgInfo: { + pkgId: 'aPackage', + }, + group, + visibility, + }; + }, + }; + }, + }; +}); + +const tsTester = [ + '@typescript-eslint/parser', + new RuleTester({ + parser: require.resolve('@typescript-eslint/parser'), + parserOptions: { + sourceType: 'module', + ecmaVersion: 2018, + ecmaFeatures: { + jsx: true, + }, + }, + }), +] as const; + +const babelTester = [ + '@babel/eslint-parser', + new RuleTester({ + parser: require.resolve('@babel/eslint-parser'), + parserOptions: { + sourceType: 'module', + ecmaVersion: 2018, + requireConfigFile: false, + babelOptions: { + presets: ['@kbn/babel-preset/node_preset'], + }, + }, + }), +] as const; + +for (const [name, tester] of [tsTester, babelTester]) { + describe(name, () => { + tester.run('@kbn/imports/no_group_crossing_imports', NoGroupCrossingImportsRule, { + valid: [ + make('observability', 'private', 'observability', 'private'), + make('security', 'private', 'security', 'private'), + make('search', 'private', 'search', 'private'), + make('observability', 'private', 'platform', 'shared'), + make('security', 'private', 'common', 'shared'), + make('platform', 'shared', 'platform', 'shared'), + make('platform', 'shared', 'platform', 'private'), + make('common', 'shared', 'common', 'shared'), + ], + + invalid: [ + { + ...make('observability', 'private', 'security', 'private'), + errors: [ + { + line: 1, + messageId: 'ILLEGAL_IMPORT', + data: { + importerPackage: 'aPackage', + importerGroup: 'observability', + importedPackage: 'aPackage', + importedGroup: 'security', + importedVisibility: 'private', + sourcePath: 'observability.private.ts', + suggestion: formatSuggestions([ + `Please review the dependencies in your module's manifest (kibana.jsonc).`, + `Relocate this module to a different group, and/or make sure it has the right 'visibility'.`, + `Address the conflicting dependencies by refactoring the code`, + ]), + }, + }, + ], + }, + { + ...make('security', 'private', 'platform', 'private'), + errors: [ + { + line: 1, + messageId: 'ILLEGAL_IMPORT', + data: { + importerPackage: 'aPackage', + importerGroup: 'security', + importedPackage: 'aPackage', + importedGroup: 'platform', + importedVisibility: 'private', + sourcePath: 'security.private.ts', + suggestion: formatSuggestions([ + `Please review the dependencies in your module's manifest (kibana.jsonc).`, + `Relocate this module to a different group, and/or make sure it has the right 'visibility'.`, + `Address the conflicting dependencies by refactoring the code`, + ]), + }, + }, + ], + }, + ], + }); + }); +} diff --git a/packages/kbn-eslint-plugin-imports/src/rules/no_group_crossing_imports.ts b/packages/kbn-eslint-plugin-imports/src/rules/no_group_crossing_imports.ts new file mode 100644 index 0000000000000..255973ab7460a --- /dev/null +++ b/packages/kbn-eslint-plugin-imports/src/rules/no_group_crossing_imports.ts @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { dirname } from 'path'; +import type { Rule } from 'eslint'; +import type { Node } from 'estree'; +import { REPO_ROOT } from '@kbn/repo-info'; + +import { visitAllImportStatements } from '../helpers/visit_all_import_statements'; +import { getSourcePath } from '../helpers/source'; +import { getRepoSourceClassifier } from '../helpers/repo_source_classifier'; +import { getImportResolver } from '../get_import_resolver'; +import { formatSuggestions } from '../helpers/report'; +import { isImportableFrom } from '../helpers/groups'; + +export const NoGroupCrossingImportsRule: Rule.RuleModule = { + meta: { + docs: { + url: 'https://github.com/elastic/kibana/blob/main/packages/kbn-eslint-plugin-imports/README.mdx#kbnimportsno_unused_imports', + }, + messages: { + ILLEGAL_IMPORT: `⚠ Illegal import statement: "{{importerPackage}}" ({{importerGroup}}) is importing "{{importedPackage}}" ({{importedGroup}}/{{importedVisibility}}). File: {{sourcePath}}\n{{suggestion}}\n`, + }, + }, + create(context) { + const resolver = getImportResolver(context); + const classifier = getRepoSourceClassifier(resolver); + const sourcePath = getSourcePath(context); + const ownDirname = dirname(sourcePath); + const self = classifier.classify(sourcePath); + const relativePath = sourcePath.replace(REPO_ROOT, '').replace(/^\//, ''); + + return visitAllImportStatements((req, { node }) => { + if ( + req === null || + // we can ignore imports using the raw-loader, they will need to be resolved but can be managed on a case by case basis + req.startsWith('!!raw-loader') + ) { + return; + } + + const result = resolver.resolve(req, ownDirname); + if (result?.type !== 'file' || result.nodeModule) { + return; + } + + const imported = classifier.classify(result.absolute); + + if (!isImportableFrom(self.group, imported.group, imported.visibility)) { + context.report({ + node: node as Node, + messageId: 'ILLEGAL_IMPORT', + data: { + importerPackage: self.pkgInfo?.pkgId ?? 'unknown', + importerGroup: self.group, + importedPackage: imported.pkgInfo?.pkgId ?? 'unknown', + importedGroup: imported.group, + importedVisibility: imported.visibility, + sourcePath: relativePath, + suggestion: formatSuggestions([ + `Please review the dependencies in your module's manifest (kibana.jsonc).`, + `Relocate this module to a different group, and/or make sure it has the right 'visibility'.`, + `Address the conflicting dependencies by refactoring the code`, + ]), + }, + }); + return; + } + }); + }, +}; diff --git a/packages/kbn-eslint-plugin-imports/src/rules/no_group_crossing_manifests.test.ts b/packages/kbn-eslint-plugin-imports/src/rules/no_group_crossing_manifests.test.ts new file mode 100644 index 0000000000000..bf75a01b222bb --- /dev/null +++ b/packages/kbn-eslint-plugin-imports/src/rules/no_group_crossing_manifests.test.ts @@ -0,0 +1,280 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { RuleTester } from 'eslint'; +import dedent from 'dedent'; +import { NoGroupCrossingManifestsRule } from './no_group_crossing_manifests'; +import { formatSuggestions } from '../helpers/report'; +import { ModuleId } from '@kbn/repo-source-classifier/src/module_id'; +import { ModuleGroup, ModuleVisibility } from '@kbn/repo-info/types'; + +const makePlugin = (filename: string) => ({ + filename, + code: dedent` + export function plugin() { + return new MyPlugin(); + } + `, +}); + +const makePluginClass = (filename: string) => ({ + filename, + code: dedent` + class MyPlugin implements Plugin { + setup() { + console.log('foo'); + } + start() { + console.log('foo'); + } + } + `, +}); + +const makeModuleByPath = ( + path: string, + group: ModuleGroup, + visibility: ModuleVisibility, + pluginOverrides: any = {} +): Record => { + const pluginId = path.split('/')[4]; + const packageId = `@kbn/${pluginId}-plugin`; + + return { + [path]: { + type: 'server package', + dirs: [], + repoRel: 'some/relative/path', + pkgInfo: { + pkgId: packageId, + pkgDir: path.split('/').slice(0, -2).join('/'), + rel: 'some/relative/path', + }, + group, + visibility, + manifest: { + type: 'plugin', + id: packageId, + owner: ['@kbn/kibana-operations'], + plugin: { + id: pluginId, + browser: true, + server: true, + ...pluginOverrides, + }, + }, + }, + }; +}; + +const makeError = (line: number, ...violations: string[]) => ({ + line, + messageId: 'ILLEGAL_MANIFEST_DEPENDENCY', + data: { + violations: violations.join('\n'), + suggestion: formatSuggestions([ + `Please review the dependencies in your plugin's manifest (kibana.jsonc).`, + `Relocate this module to a different group, and/or make sure it has the right 'visibility'.`, + `Address the conflicting dependencies by refactoring the code`, + ]), + }, +}); + +jest.mock('../helpers/repo_source_classifier', () => { + const MODULES_BY_PATH: Record = { + ...makeModuleByPath( + 'path/to/search/plugins/searchPlugin1/server/index.ts', + 'search', + 'private', + { + requiredPlugins: ['searchPlugin2'], // allowed, same group + } + ), + ...makeModuleByPath( + 'path/to/search/plugins/searchPlugin2/server/index.ts', + 'search', + 'private', + { + requiredPlugins: ['securityPlugin1'], // invalid, dependency belongs to another group + } + ), + ...makeModuleByPath( + 'path/to/security/plugins/securityPlugin1/server/index.ts', + 'security', + 'private', + { + requiredPlugins: ['securityPlugin2'], // allowed, same group + } + ), + ...makeModuleByPath( + 'path/to/security/plugins/securityPlugin2/server/index.ts', + 'security', + 'private', + { + requiredPlugins: ['platformPlugin1', 'platformPlugin2', 'platformPlugin3'], // 3rd one is private! + } + ), + ...makeModuleByPath( + 'path/to/platform/shared/platformPlugin1/server/index.ts', + 'platform', + 'shared', + { + requiredPlugins: ['platformPlugin2', 'platformPlugin3', 'platformPlugin4'], + } + ), + ...makeModuleByPath( + 'path/to/platform/shared/platformPlugin2/server/index.ts', + 'platform', + 'shared' + ), + ...makeModuleByPath( + 'path/to/platform/private/platformPlugin3/server/index.ts', + 'platform', + 'private' + ), + ...makeModuleByPath( + 'path/to/platform/private/platformPlugin4/server/index.ts', + 'platform', + 'private' + ), + }; + + return { + getRepoSourceClassifier() { + return { + classify(path: string) { + return MODULES_BY_PATH[path]; + }, + }; + }, + }; +}); + +jest.mock('@kbn/repo-packages', () => { + const original = jest.requireActual('@kbn/repo-packages'); + + return { + ...original, + getPluginPackagesFilter: () => () => true, + getPackages() { + return [ + 'path/to/search/plugins/searchPlugin1/server/index.ts', + 'path/to/search/plugins/searchPlugin2/server/index.ts', + 'path/to/security/plugins/securityPlugin1/server/index.ts', + 'path/to/security/plugins/securityPlugin2/server/index.ts', + 'path/to/platform/shared/platformPlugin1/server/index.ts', + 'path/to/platform/shared/platformPlugin2/server/index.ts', + 'path/to/platform/private/platformPlugin3/server/index.ts', + 'path/to/platform/private/platformPlugin4/server/index.ts', + ].map((path) => { + const [, , group, , id] = path.split('/'); + return { + id: `@kbn/${id}-plugin`, + group, + visibility: path.includes('platform/shared') ? 'shared' : 'private', + manifest: { + plugin: { + id, + }, + }, + }; + }); + }, + }; +}); + +const tsTester = [ + '@typescript-eslint/parser', + new RuleTester({ + parser: require.resolve('@typescript-eslint/parser'), + parserOptions: { + sourceType: 'module', + ecmaVersion: 2018, + ecmaFeatures: { + jsx: true, + }, + }, + }), +] as const; + +const babelTester = [ + '@babel/eslint-parser', + new RuleTester({ + parser: require.resolve('@babel/eslint-parser'), + parserOptions: { + sourceType: 'module', + ecmaVersion: 2018, + requireConfigFile: false, + babelOptions: { + presets: ['@kbn/babel-preset/node_preset'], + }, + }, + }), +] as const; + +for (const [name, tester] of [tsTester, babelTester]) { + describe(name, () => { + tester.run('@kbn/imports/no_group_crossing_manifests', NoGroupCrossingManifestsRule, { + valid: [ + makePlugin('path/to/search/plugins/searchPlugin1/server/index.ts'), + makePlugin('path/to/security/plugins/securityPlugin1/server/index.ts'), + makePlugin('path/to/platform/shared/platformPlugin1/server/index.ts'), + makePluginClass('path/to/search/plugins/searchPlugin1/server/index.ts'), + makePluginClass('path/to/security/plugins/securityPlugin1/server/index.ts'), + makePluginClass('path/to/platform/shared/platformPlugin1/server/index.ts'), + ], + invalid: [ + { + ...makePlugin('path/to/search/plugins/searchPlugin2/server/index.ts'), + errors: [ + makeError( + 1, + `⚠ Illegal dependency on manifest: Plugin "searchPlugin2" (package: "@kbn/searchPlugin2-plugin"; group: "search") depends on "securityPlugin1" (package: "@kbn/securityPlugin1-plugin"; group: security/private). File: path/to/search/plugins/searchPlugin2/kibana.jsonc` + ), + ], + }, + { + ...makePlugin('path/to/security/plugins/securityPlugin2/server/index.ts'), + errors: [ + makeError( + 1, + `⚠ Illegal dependency on manifest: Plugin "securityPlugin2" (package: "@kbn/securityPlugin2-plugin"; group: "security") depends on "platformPlugin3" (package: "@kbn/platformPlugin3-plugin"; group: platform/private). File: path/to/security/plugins/securityPlugin2/kibana.jsonc` + ), + ], + }, + { + ...makePluginClass('path/to/search/plugins/searchPlugin2/server/index.ts'), + errors: [ + makeError( + 2, + `⚠ Illegal dependency on manifest: Plugin "searchPlugin2" (package: "@kbn/searchPlugin2-plugin"; group: "search") depends on "securityPlugin1" (package: "@kbn/securityPlugin1-plugin"; group: security/private). File: path/to/search/plugins/searchPlugin2/kibana.jsonc` + ), + makeError( + 5, + `⚠ Illegal dependency on manifest: Plugin "searchPlugin2" (package: "@kbn/searchPlugin2-plugin"; group: "search") depends on "securityPlugin1" (package: "@kbn/securityPlugin1-plugin"; group: security/private). File: path/to/search/plugins/searchPlugin2/kibana.jsonc` + ), + ], + }, + { + ...makePluginClass('path/to/security/plugins/securityPlugin2/server/index.ts'), + errors: [ + makeError( + 2, + `⚠ Illegal dependency on manifest: Plugin "securityPlugin2" (package: "@kbn/securityPlugin2-plugin"; group: "security") depends on "platformPlugin3" (package: "@kbn/platformPlugin3-plugin"; group: platform/private). File: path/to/security/plugins/securityPlugin2/kibana.jsonc` + ), + makeError( + 5, + `⚠ Illegal dependency on manifest: Plugin "securityPlugin2" (package: "@kbn/securityPlugin2-plugin"; group: "security") depends on "platformPlugin3" (package: "@kbn/platformPlugin3-plugin"; group: platform/private). File: path/to/security/plugins/securityPlugin2/kibana.jsonc` + ), + ], + }, + ], + }); + }); +} diff --git a/packages/kbn-eslint-plugin-imports/src/rules/no_group_crossing_manifests.ts b/packages/kbn-eslint-plugin-imports/src/rules/no_group_crossing_manifests.ts new file mode 100644 index 0000000000000..e68f7217905a5 --- /dev/null +++ b/packages/kbn-eslint-plugin-imports/src/rules/no_group_crossing_manifests.ts @@ -0,0 +1,158 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { join } from 'path'; +import { TSESTree } from '@typescript-eslint/typescript-estree'; +import type { Rule } from 'eslint'; +import type { Node } from 'estree'; +import { getPackages, getPluginPackagesFilter } from '@kbn/repo-packages'; +import { REPO_ROOT } from '@kbn/repo-info'; +import type { ModuleGroup, ModuleVisibility } from '@kbn/repo-info/types'; +import { getSourcePath } from '../helpers/source'; +import { getImportResolver } from '../get_import_resolver'; +import { getRepoSourceClassifier } from '../helpers/repo_source_classifier'; +import { isImportableFrom } from '../helpers/groups'; +import { formatSuggestions } from '../helpers/report'; + +const NODE_TYPES = TSESTree.AST_NODE_TYPES; + +interface PluginInfo { + id: string; + pluginId: string; + group: ModuleGroup; + visibility: ModuleVisibility; +} + +export const NoGroupCrossingManifestsRule: Rule.RuleModule = { + meta: { + docs: { + url: 'https://github.com/elastic/kibana/blob/main/packages/kbn-eslint-plugin-imports/README.mdx#kbnimportsno_unused_imports', + }, + messages: { + ILLEGAL_MANIFEST_DEPENDENCY: `{{violations}}\n{{suggestion}}`, + }, + }, + create(context) { + const sourcePath = getSourcePath(context); + let manifestPath: string; + const resolver = getImportResolver(context); + const classifier = getRepoSourceClassifier(resolver); + const moduleId = classifier.classify(sourcePath); + const offendingDependencies: PluginInfo[] = []; + let currentPlugin: PluginInfo; + + if (moduleId.manifest?.type === 'plugin') { + manifestPath = join(moduleId.pkgInfo!.pkgDir, 'kibana.jsonc') + .replace(REPO_ROOT, '') + .replace(/^\//, ''); + currentPlugin = { + id: moduleId.pkgInfo!.pkgId, + pluginId: moduleId.manifest.plugin.id, + group: moduleId.group, + visibility: moduleId.visibility, + }; + + const allPlugins = getPackages(REPO_ROOT).filter(getPluginPackagesFilter()); + const currentPluginInfo = moduleId.manifest!.plugin; + // check all the dependencies in the manifest, looking for plugin violations + [ + ...(currentPluginInfo.requiredPlugins ?? []), + ...(currentPluginInfo.requiredBundles ?? []), + ...(currentPluginInfo.optionalPlugins ?? []), + ...(currentPluginInfo.runtimePluginDependencies ?? []), + ].forEach((pluginId) => { + const dependency = allPlugins.find(({ manifest }) => manifest.plugin.id === pluginId); + if (dependency) { + // at this point, we know the dependency is a plugin + const { id, group, visibility } = dependency; + if (!isImportableFrom(moduleId.group, group, visibility)) { + offendingDependencies.push({ id, pluginId, group, visibility }); + } + } + }); + } + + return { + FunctionDeclaration(node) { + // complain in exported plugin() function + if ( + currentPlugin && + offendingDependencies.length && + node.id?.name === 'plugin' && + node.parent.type === NODE_TYPES.ExportNamedDeclaration + ) { + reportViolation({ + context, + node, + currentPlugin, + manifestPath, + offendingDependencies, + }); + } + }, + MethodDefinition(node) { + // complain in setup() and start() hooks + if ( + offendingDependencies.length && + node.key.type === NODE_TYPES.Identifier && + (node.key.name === 'setup' || node.key.name === 'start') && + node.kind === 'method' && + node.parent.parent.type === NODE_TYPES.ClassDeclaration && + (node.parent.parent.id?.name.includes('Plugin') || + (node.parent.parent as TSESTree.ClassDeclaration).implements?.find( + (value) => + value.expression.type === NODE_TYPES.Identifier && + value.expression.name === 'Plugin' + )) + ) { + reportViolation({ + context, + node, + currentPlugin, + manifestPath, + offendingDependencies, + }); + } + }, + }; + }, +}; + +interface ReportViolationParams { + context: Rule.RuleContext; + node: Node; + currentPlugin: PluginInfo; + offendingDependencies: PluginInfo[]; + manifestPath: string; +} + +const reportViolation = ({ + context, + node, + currentPlugin, + offendingDependencies, + manifestPath, +}: ReportViolationParams) => + context.report({ + node, + messageId: 'ILLEGAL_MANIFEST_DEPENDENCY', + data: { + violations: [ + ...offendingDependencies.map( + ({ id, pluginId, group, visibility }) => + `⚠ Illegal dependency on manifest: Plugin "${currentPlugin.pluginId}" (package: "${currentPlugin.id}"; group: "${currentPlugin.group}") depends on "${pluginId}" (package: "${id}"; group: ${group}/${visibility}). File: ${manifestPath}` + ), + ].join('\n'), + suggestion: formatSuggestions([ + `Please review the dependencies in your plugin's manifest (kibana.jsonc).`, + `Relocate this module to a different group, and/or make sure it has the right 'visibility'.`, + `Address the conflicting dependencies by refactoring the code`, + ]), + }, + }); diff --git a/packages/kbn-eslint-plugin-imports/tsconfig.json b/packages/kbn-eslint-plugin-imports/tsconfig.json index 087d77fbfe437..b0ab9182171c3 100644 --- a/packages/kbn-eslint-plugin-imports/tsconfig.json +++ b/packages/kbn-eslint-plugin-imports/tsconfig.json @@ -14,6 +14,7 @@ "@kbn/import-resolver", "@kbn/repo-source-classifier", "@kbn/repo-info", + "@kbn/repo-packages", ], "exclude": [ "target/**/*", diff --git a/packages/kbn-esql-ast/src/antlr/esql_lexer.g4 b/packages/kbn-esql-ast/src/antlr/esql_lexer.g4 index 80a30301d080c..0ffee7c0b0d4f 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_lexer.g4 +++ b/packages/kbn-esql-ast/src/antlr/esql_lexer.g4 @@ -88,7 +88,6 @@ WHERE : 'where' -> pushMode(EXPRESSION_MODE); // MYCOMMAND : 'mycommand' -> ... DEV_INLINESTATS : {this.isDevVersion()}? 'inlinestats' -> pushMode(EXPRESSION_MODE); DEV_LOOKUP : {this.isDevVersion()}? 'lookup' -> pushMode(LOOKUP_MODE); -DEV_MATCH : {this.isDevVersion()}? 'match' -> pushMode(EXPRESSION_MODE); DEV_METRICS : {this.isDevVersion()}? 'metrics' -> pushMode(METRICS_MODE); // @@ -211,8 +210,8 @@ ASTERISK : '*'; SLASH : '/'; PERCENT : '%'; -// move it in the main section if the feature gets promoted -DEV_MATCH_OP : {this.isDevVersion()}? DEV_MATCH -> type(DEV_MATCH); +MATCH : 'match'; +NESTED_WHERE : WHERE -> type(WHERE); NAMED_OR_POSITIONAL_PARAM : PARAM (LETTER | UNDERSCORE) UNQUOTED_ID_BODY* @@ -308,8 +307,8 @@ mode PROJECT_MODE; PROJECT_PIPE : PIPE -> type(PIPE), popMode; PROJECT_DOT: DOT -> type(DOT); PROJECT_COMMA : COMMA -> type(COMMA); -PROJECT_PARAM : PARAM -> type(PARAM); -PROJECT_NAMED_OR_POSITIONAL_PARAM : NAMED_OR_POSITIONAL_PARAM -> type(NAMED_OR_POSITIONAL_PARAM); +PROJECT_PARAM : {this.isDevVersion()}? PARAM -> type(PARAM); +PROJECT_NAMED_OR_POSITIONAL_PARAM : {this.isDevVersion()}? NAMED_OR_POSITIONAL_PARAM -> type(NAMED_OR_POSITIONAL_PARAM); fragment UNQUOTED_ID_BODY_WITH_PATTERN : (LETTER | DIGIT | UNDERSCORE | ASTERISK) @@ -343,8 +342,8 @@ RENAME_PIPE : PIPE -> type(PIPE), popMode; RENAME_ASSIGN : ASSIGN -> type(ASSIGN); RENAME_COMMA : COMMA -> type(COMMA); RENAME_DOT: DOT -> type(DOT); -RENAME_PARAM : PARAM -> type(PARAM); -RENAME_NAMED_OR_POSITIONAL_PARAM : NAMED_OR_POSITIONAL_PARAM -> type(NAMED_OR_POSITIONAL_PARAM); +RENAME_PARAM : {this.isDevVersion()}? PARAM -> type(PARAM); +RENAME_NAMED_OR_POSITIONAL_PARAM : {this.isDevVersion()}? NAMED_OR_POSITIONAL_PARAM -> type(NAMED_OR_POSITIONAL_PARAM); AS : 'as'; @@ -416,8 +415,8 @@ ENRICH_FIELD_QUOTED_IDENTIFIER : QUOTED_IDENTIFIER -> type(QUOTED_IDENTIFIER) ; -ENRICH_FIELD_PARAM : PARAM -> type(PARAM); -ENRICH_FIELD_NAMED_OR_POSITIONAL_PARAM : NAMED_OR_POSITIONAL_PARAM -> type(NAMED_OR_POSITIONAL_PARAM); +ENRICH_FIELD_PARAM : {this.isDevVersion()}? PARAM -> type(PARAM); +ENRICH_FIELD_NAMED_OR_POSITIONAL_PARAM : {this.isDevVersion()}? NAMED_OR_POSITIONAL_PARAM -> type(NAMED_OR_POSITIONAL_PARAM); ENRICH_FIELD_LINE_COMMENT : LINE_COMMENT -> channel(HIDDEN) @@ -434,8 +433,8 @@ ENRICH_FIELD_WS mode MVEXPAND_MODE; MVEXPAND_PIPE : PIPE -> type(PIPE), popMode; MVEXPAND_DOT: DOT -> type(DOT); -MVEXPAND_PARAM : PARAM -> type(PARAM); -MVEXPAND_NAMED_OR_POSITIONAL_PARAM : NAMED_OR_POSITIONAL_PARAM -> type(NAMED_OR_POSITIONAL_PARAM); +MVEXPAND_PARAM : {this.isDevVersion()}? PARAM -> type(PARAM); +MVEXPAND_NAMED_OR_POSITIONAL_PARAM : {this.isDevVersion()}? NAMED_OR_POSITIONAL_PARAM -> type(NAMED_OR_POSITIONAL_PARAM); MVEXPAND_QUOTED_IDENTIFIER : QUOTED_IDENTIFIER -> type(QUOTED_IDENTIFIER) diff --git a/packages/kbn-esql-ast/src/antlr/esql_lexer.interp b/packages/kbn-esql-ast/src/antlr/esql_lexer.interp index b5ca44826c051..2566da379af73 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_lexer.interp +++ b/packages/kbn-esql-ast/src/antlr/esql_lexer.interp @@ -23,7 +23,6 @@ null null null null -null '|' null null @@ -63,6 +62,7 @@ null '*' '/' '%' +'match' null null ']' @@ -141,7 +141,6 @@ STATS WHERE DEV_INLINESTATS DEV_LOOKUP -DEV_MATCH DEV_METRICS UNKNOWN_CMD LINE_COMMENT @@ -186,6 +185,7 @@ MINUS ASTERISK SLASH PERCENT +MATCH NAMED_OR_POSITIONAL_PARAM OPENING_BRACKET CLOSING_BRACKET @@ -263,7 +263,6 @@ STATS WHERE DEV_INLINESTATS DEV_LOOKUP -DEV_MATCH DEV_METRICS UNKNOWN_CMD LINE_COMMENT @@ -318,7 +317,8 @@ MINUS ASTERISK SLASH PERCENT -DEV_MATCH_OP +MATCH +NESTED_WHERE NAMED_OR_POSITIONAL_PARAM OPENING_BRACKET CLOSING_BRACKET @@ -466,4 +466,4 @@ METRICS_MODE CLOSING_METRICS_MODE atn: -[4, 0, 120, 1475, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 2, 85, 7, 85, 2, 86, 7, 86, 2, 87, 7, 87, 2, 88, 7, 88, 2, 89, 7, 89, 2, 90, 7, 90, 2, 91, 7, 91, 2, 92, 7, 92, 2, 93, 7, 93, 2, 94, 7, 94, 2, 95, 7, 95, 2, 96, 7, 96, 2, 97, 7, 97, 2, 98, 7, 98, 2, 99, 7, 99, 2, 100, 7, 100, 2, 101, 7, 101, 2, 102, 7, 102, 2, 103, 7, 103, 2, 104, 7, 104, 2, 105, 7, 105, 2, 106, 7, 106, 2, 107, 7, 107, 2, 108, 7, 108, 2, 109, 7, 109, 2, 110, 7, 110, 2, 111, 7, 111, 2, 112, 7, 112, 2, 113, 7, 113, 2, 114, 7, 114, 2, 115, 7, 115, 2, 116, 7, 116, 2, 117, 7, 117, 2, 118, 7, 118, 2, 119, 7, 119, 2, 120, 7, 120, 2, 121, 7, 121, 2, 122, 7, 122, 2, 123, 7, 123, 2, 124, 7, 124, 2, 125, 7, 125, 2, 126, 7, 126, 2, 127, 7, 127, 2, 128, 7, 128, 2, 129, 7, 129, 2, 130, 7, 130, 2, 131, 7, 131, 2, 132, 7, 132, 2, 133, 7, 133, 2, 134, 7, 134, 2, 135, 7, 135, 2, 136, 7, 136, 2, 137, 7, 137, 2, 138, 7, 138, 2, 139, 7, 139, 2, 140, 7, 140, 2, 141, 7, 141, 2, 142, 7, 142, 2, 143, 7, 143, 2, 144, 7, 144, 2, 145, 7, 145, 2, 146, 7, 146, 2, 147, 7, 147, 2, 148, 7, 148, 2, 149, 7, 149, 2, 150, 7, 150, 2, 151, 7, 151, 2, 152, 7, 152, 2, 153, 7, 153, 2, 154, 7, 154, 2, 155, 7, 155, 2, 156, 7, 156, 2, 157, 7, 157, 2, 158, 7, 158, 2, 159, 7, 159, 2, 160, 7, 160, 2, 161, 7, 161, 2, 162, 7, 162, 2, 163, 7, 163, 2, 164, 7, 164, 2, 165, 7, 165, 2, 166, 7, 166, 2, 167, 7, 167, 2, 168, 7, 168, 2, 169, 7, 169, 2, 170, 7, 170, 2, 171, 7, 171, 2, 172, 7, 172, 2, 173, 7, 173, 2, 174, 7, 174, 2, 175, 7, 175, 2, 176, 7, 176, 2, 177, 7, 177, 2, 178, 7, 178, 2, 179, 7, 179, 2, 180, 7, 180, 2, 181, 7, 181, 2, 182, 7, 182, 2, 183, 7, 183, 2, 184, 7, 184, 2, 185, 7, 185, 2, 186, 7, 186, 2, 187, 7, 187, 2, 188, 7, 188, 2, 189, 7, 189, 2, 190, 7, 190, 2, 191, 7, 191, 2, 192, 7, 192, 2, 193, 7, 193, 2, 194, 7, 194, 2, 195, 7, 195, 2, 196, 7, 196, 2, 197, 7, 197, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 20, 4, 20, 587, 8, 20, 11, 20, 12, 20, 588, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 5, 21, 597, 8, 21, 10, 21, 12, 21, 600, 9, 21, 1, 21, 3, 21, 603, 8, 21, 1, 21, 3, 21, 606, 8, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 5, 22, 615, 8, 22, 10, 22, 12, 22, 618, 9, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 4, 23, 626, 8, 23, 11, 23, 12, 23, 627, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 29, 1, 29, 3, 29, 647, 8, 29, 1, 29, 4, 29, 650, 8, 29, 11, 29, 12, 29, 651, 1, 30, 1, 30, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 3, 32, 661, 8, 32, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 3, 34, 668, 8, 34, 1, 35, 1, 35, 1, 35, 5, 35, 673, 8, 35, 10, 35, 12, 35, 676, 9, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 5, 35, 684, 8, 35, 10, 35, 12, 35, 687, 9, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 3, 35, 694, 8, 35, 1, 35, 3, 35, 697, 8, 35, 3, 35, 699, 8, 35, 1, 36, 4, 36, 702, 8, 36, 11, 36, 12, 36, 703, 1, 37, 4, 37, 707, 8, 37, 11, 37, 12, 37, 708, 1, 37, 1, 37, 5, 37, 713, 8, 37, 10, 37, 12, 37, 716, 9, 37, 1, 37, 1, 37, 4, 37, 720, 8, 37, 11, 37, 12, 37, 721, 1, 37, 4, 37, 725, 8, 37, 11, 37, 12, 37, 726, 1, 37, 1, 37, 5, 37, 731, 8, 37, 10, 37, 12, 37, 734, 9, 37, 3, 37, 736, 8, 37, 1, 37, 1, 37, 1, 37, 1, 37, 4, 37, 742, 8, 37, 11, 37, 12, 37, 743, 1, 37, 1, 37, 3, 37, 748, 8, 37, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 62, 1, 62, 1, 62, 1, 63, 1, 63, 1, 63, 1, 64, 1, 64, 1, 65, 1, 65, 1, 65, 1, 66, 1, 66, 1, 67, 1, 67, 1, 67, 1, 68, 1, 68, 1, 69, 1, 69, 1, 70, 1, 70, 1, 71, 1, 71, 1, 72, 1, 72, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 74, 1, 74, 1, 74, 3, 74, 875, 8, 74, 1, 74, 5, 74, 878, 8, 74, 10, 74, 12, 74, 881, 9, 74, 1, 74, 1, 74, 4, 74, 885, 8, 74, 11, 74, 12, 74, 886, 3, 74, 889, 8, 74, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 77, 1, 77, 5, 77, 903, 8, 77, 10, 77, 12, 77, 906, 9, 77, 1, 77, 1, 77, 3, 77, 910, 8, 77, 1, 77, 4, 77, 913, 8, 77, 11, 77, 12, 77, 914, 3, 77, 917, 8, 77, 1, 78, 1, 78, 4, 78, 921, 8, 78, 11, 78, 12, 78, 922, 1, 78, 1, 78, 1, 79, 1, 79, 1, 80, 1, 80, 1, 80, 1, 80, 1, 81, 1, 81, 1, 81, 1, 81, 1, 82, 1, 82, 1, 82, 1, 82, 1, 83, 1, 83, 1, 83, 1, 83, 1, 83, 1, 84, 1, 84, 1, 84, 1, 84, 1, 84, 1, 85, 1, 85, 1, 85, 1, 85, 1, 86, 1, 86, 1, 86, 1, 86, 1, 87, 1, 87, 1, 87, 1, 87, 1, 88, 1, 88, 1, 88, 1, 88, 1, 88, 1, 89, 1, 89, 1, 89, 1, 89, 1, 90, 1, 90, 1, 90, 1, 90, 1, 91, 1, 91, 1, 91, 1, 91, 1, 92, 1, 92, 1, 92, 1, 92, 1, 93, 1, 93, 1, 93, 1, 93, 1, 94, 1, 94, 1, 94, 1, 94, 1, 94, 1, 94, 1, 94, 1, 94, 1, 94, 1, 95, 1, 95, 1, 95, 3, 95, 1000, 8, 95, 1, 96, 4, 96, 1003, 8, 96, 11, 96, 12, 96, 1004, 1, 97, 1, 97, 1, 97, 1, 97, 1, 98, 1, 98, 1, 98, 1, 98, 1, 99, 1, 99, 1, 99, 1, 99, 1, 100, 1, 100, 1, 100, 1, 100, 1, 101, 1, 101, 1, 101, 1, 101, 1, 102, 1, 102, 1, 102, 1, 102, 1, 102, 1, 103, 1, 103, 1, 103, 1, 103, 1, 104, 1, 104, 1, 104, 1, 104, 1, 105, 1, 105, 1, 105, 1, 105, 1, 106, 1, 106, 1, 106, 1, 106, 1, 107, 1, 107, 1, 107, 1, 107, 3, 107, 1052, 8, 107, 1, 108, 1, 108, 3, 108, 1056, 8, 108, 1, 108, 5, 108, 1059, 8, 108, 10, 108, 12, 108, 1062, 9, 108, 1, 108, 1, 108, 3, 108, 1066, 8, 108, 1, 108, 4, 108, 1069, 8, 108, 11, 108, 12, 108, 1070, 3, 108, 1073, 8, 108, 1, 109, 1, 109, 4, 109, 1077, 8, 109, 11, 109, 12, 109, 1078, 1, 110, 1, 110, 1, 110, 1, 110, 1, 111, 1, 111, 1, 111, 1, 111, 1, 112, 1, 112, 1, 112, 1, 112, 1, 113, 1, 113, 1, 113, 1, 113, 1, 113, 1, 114, 1, 114, 1, 114, 1, 114, 1, 115, 1, 115, 1, 115, 1, 115, 1, 116, 1, 116, 1, 116, 1, 116, 1, 117, 1, 117, 1, 117, 1, 117, 1, 118, 1, 118, 1, 118, 1, 118, 1, 119, 1, 119, 1, 119, 1, 120, 1, 120, 1, 120, 1, 120, 1, 121, 1, 121, 1, 121, 1, 121, 1, 122, 1, 122, 1, 122, 1, 122, 1, 123, 1, 123, 1, 123, 1, 123, 1, 124, 1, 124, 1, 124, 1, 124, 1, 124, 1, 125, 1, 125, 1, 125, 1, 125, 1, 125, 1, 126, 1, 126, 1, 126, 1, 126, 1, 126, 1, 127, 1, 127, 1, 127, 1, 127, 1, 127, 1, 127, 1, 127, 1, 128, 1, 128, 1, 129, 4, 129, 1162, 8, 129, 11, 129, 12, 129, 1163, 1, 129, 1, 129, 3, 129, 1168, 8, 129, 1, 129, 4, 129, 1171, 8, 129, 11, 129, 12, 129, 1172, 1, 130, 1, 130, 1, 130, 1, 130, 1, 131, 1, 131, 1, 131, 1, 131, 1, 132, 1, 132, 1, 132, 1, 132, 1, 133, 1, 133, 1, 133, 1, 133, 1, 134, 1, 134, 1, 134, 1, 134, 1, 134, 1, 134, 1, 135, 1, 135, 1, 135, 1, 135, 1, 136, 1, 136, 1, 136, 1, 136, 1, 137, 1, 137, 1, 137, 1, 137, 1, 138, 1, 138, 1, 138, 1, 138, 1, 139, 1, 139, 1, 139, 1, 139, 1, 140, 1, 140, 1, 140, 1, 140, 1, 141, 1, 141, 1, 141, 1, 141, 1, 142, 1, 142, 1, 142, 1, 142, 1, 143, 1, 143, 1, 143, 1, 143, 1, 144, 1, 144, 1, 144, 1, 144, 1, 145, 1, 145, 1, 145, 1, 145, 1, 146, 1, 146, 1, 146, 1, 146, 1, 146, 1, 147, 1, 147, 1, 147, 1, 147, 1, 148, 1, 148, 1, 148, 1, 148, 1, 149, 1, 149, 1, 149, 1, 149, 1, 150, 1, 150, 1, 150, 1, 150, 1, 151, 1, 151, 1, 151, 1, 151, 1, 152, 1, 152, 1, 152, 1, 152, 1, 153, 1, 153, 1, 153, 1, 153, 1, 154, 1, 154, 1, 154, 1, 154, 1, 155, 1, 155, 1, 155, 1, 155, 1, 155, 1, 156, 1, 156, 1, 156, 1, 156, 1, 156, 1, 157, 1, 157, 1, 157, 1, 157, 1, 158, 1, 158, 1, 158, 1, 158, 1, 159, 1, 159, 1, 159, 1, 159, 1, 160, 1, 160, 1, 160, 1, 160, 1, 160, 1, 161, 1, 161, 1, 162, 1, 162, 1, 162, 1, 162, 1, 162, 4, 162, 1312, 8, 162, 11, 162, 12, 162, 1313, 1, 163, 1, 163, 1, 163, 1, 163, 1, 164, 1, 164, 1, 164, 1, 164, 1, 165, 1, 165, 1, 165, 1, 165, 1, 166, 1, 166, 1, 166, 1, 166, 1, 166, 1, 167, 1, 167, 1, 167, 1, 167, 1, 168, 1, 168, 1, 168, 1, 168, 1, 169, 1, 169, 1, 169, 1, 169, 1, 170, 1, 170, 1, 170, 1, 170, 1, 170, 1, 171, 1, 171, 1, 171, 1, 171, 1, 172, 1, 172, 1, 172, 1, 172, 1, 173, 1, 173, 1, 173, 1, 173, 1, 174, 1, 174, 1, 174, 1, 174, 1, 175, 1, 175, 1, 175, 1, 175, 1, 176, 1, 176, 1, 176, 1, 176, 1, 176, 1, 176, 1, 177, 1, 177, 1, 177, 1, 177, 1, 178, 1, 178, 1, 178, 1, 178, 1, 179, 1, 179, 1, 179, 1, 179, 1, 180, 1, 180, 1, 180, 1, 180, 1, 181, 1, 181, 1, 181, 1, 181, 1, 182, 1, 182, 1, 182, 1, 182, 1, 183, 1, 183, 1, 183, 1, 183, 1, 183, 1, 184, 1, 184, 1, 184, 1, 184, 1, 184, 1, 184, 1, 185, 1, 185, 1, 185, 1, 185, 1, 185, 1, 185, 1, 186, 1, 186, 1, 186, 1, 186, 1, 187, 1, 187, 1, 187, 1, 187, 1, 188, 1, 188, 1, 188, 1, 188, 1, 189, 1, 189, 1, 189, 1, 189, 1, 189, 1, 189, 1, 190, 1, 190, 1, 190, 1, 190, 1, 190, 1, 190, 1, 191, 1, 191, 1, 191, 1, 191, 1, 192, 1, 192, 1, 192, 1, 192, 1, 193, 1, 193, 1, 193, 1, 193, 1, 194, 1, 194, 1, 194, 1, 194, 1, 194, 1, 194, 1, 195, 1, 195, 1, 195, 1, 195, 1, 195, 1, 195, 1, 196, 1, 196, 1, 196, 1, 196, 1, 196, 1, 196, 1, 197, 1, 197, 1, 197, 1, 197, 1, 197, 2, 616, 685, 0, 198, 15, 1, 17, 2, 19, 3, 21, 4, 23, 5, 25, 6, 27, 7, 29, 8, 31, 9, 33, 10, 35, 11, 37, 12, 39, 13, 41, 14, 43, 15, 45, 16, 47, 17, 49, 18, 51, 19, 53, 20, 55, 21, 57, 22, 59, 23, 61, 24, 63, 25, 65, 0, 67, 0, 69, 0, 71, 0, 73, 0, 75, 0, 77, 0, 79, 0, 81, 0, 83, 0, 85, 26, 87, 27, 89, 28, 91, 29, 93, 30, 95, 31, 97, 32, 99, 33, 101, 34, 103, 35, 105, 36, 107, 37, 109, 38, 111, 39, 113, 40, 115, 41, 117, 42, 119, 43, 121, 44, 123, 45, 125, 46, 127, 47, 129, 48, 131, 49, 133, 50, 135, 51, 137, 52, 139, 53, 141, 54, 143, 55, 145, 56, 147, 57, 149, 58, 151, 59, 153, 60, 155, 61, 157, 62, 159, 63, 161, 0, 163, 64, 165, 65, 167, 66, 169, 67, 171, 0, 173, 68, 175, 69, 177, 70, 179, 71, 181, 0, 183, 0, 185, 72, 187, 73, 189, 74, 191, 0, 193, 0, 195, 0, 197, 0, 199, 0, 201, 0, 203, 75, 205, 0, 207, 76, 209, 0, 211, 0, 213, 77, 215, 78, 217, 79, 219, 0, 221, 0, 223, 0, 225, 0, 227, 0, 229, 0, 231, 0, 233, 80, 235, 81, 237, 82, 239, 83, 241, 0, 243, 0, 245, 0, 247, 0, 249, 0, 251, 0, 253, 84, 255, 0, 257, 85, 259, 86, 261, 87, 263, 0, 265, 0, 267, 88, 269, 89, 271, 0, 273, 90, 275, 0, 277, 91, 279, 92, 281, 93, 283, 0, 285, 0, 287, 0, 289, 0, 291, 0, 293, 0, 295, 0, 297, 0, 299, 0, 301, 94, 303, 95, 305, 96, 307, 0, 309, 0, 311, 0, 313, 0, 315, 0, 317, 0, 319, 97, 321, 98, 323, 99, 325, 0, 327, 100, 329, 101, 331, 102, 333, 103, 335, 0, 337, 104, 339, 105, 341, 106, 343, 107, 345, 108, 347, 0, 349, 0, 351, 0, 353, 0, 355, 0, 357, 0, 359, 0, 361, 109, 363, 110, 365, 111, 367, 0, 369, 0, 371, 0, 373, 0, 375, 112, 377, 113, 379, 114, 381, 0, 383, 0, 385, 0, 387, 115, 389, 116, 391, 117, 393, 0, 395, 0, 397, 118, 399, 119, 401, 120, 403, 0, 405, 0, 407, 0, 409, 0, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 35, 2, 0, 68, 68, 100, 100, 2, 0, 73, 73, 105, 105, 2, 0, 83, 83, 115, 115, 2, 0, 69, 69, 101, 101, 2, 0, 67, 67, 99, 99, 2, 0, 84, 84, 116, 116, 2, 0, 82, 82, 114, 114, 2, 0, 79, 79, 111, 111, 2, 0, 80, 80, 112, 112, 2, 0, 78, 78, 110, 110, 2, 0, 72, 72, 104, 104, 2, 0, 86, 86, 118, 118, 2, 0, 65, 65, 97, 97, 2, 0, 76, 76, 108, 108, 2, 0, 88, 88, 120, 120, 2, 0, 70, 70, 102, 102, 2, 0, 77, 77, 109, 109, 2, 0, 71, 71, 103, 103, 2, 0, 75, 75, 107, 107, 2, 0, 87, 87, 119, 119, 2, 0, 85, 85, 117, 117, 6, 0, 9, 10, 13, 13, 32, 32, 47, 47, 91, 91, 93, 93, 2, 0, 10, 10, 13, 13, 3, 0, 9, 10, 13, 13, 32, 32, 1, 0, 48, 57, 2, 0, 65, 90, 97, 122, 8, 0, 34, 34, 78, 78, 82, 82, 84, 84, 92, 92, 110, 110, 114, 114, 116, 116, 4, 0, 10, 10, 13, 13, 34, 34, 92, 92, 2, 0, 43, 43, 45, 45, 1, 0, 96, 96, 2, 0, 66, 66, 98, 98, 2, 0, 89, 89, 121, 121, 11, 0, 9, 10, 13, 13, 32, 32, 34, 34, 44, 44, 47, 47, 58, 58, 61, 61, 91, 91, 93, 93, 124, 124, 2, 0, 42, 42, 47, 47, 11, 0, 9, 10, 13, 13, 32, 32, 34, 35, 44, 44, 47, 47, 58, 58, 60, 60, 62, 63, 92, 92, 124, 124, 1503, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 1, 63, 1, 0, 0, 0, 1, 85, 1, 0, 0, 0, 1, 87, 1, 0, 0, 0, 1, 89, 1, 0, 0, 0, 1, 91, 1, 0, 0, 0, 1, 93, 1, 0, 0, 0, 1, 95, 1, 0, 0, 0, 1, 97, 1, 0, 0, 0, 1, 99, 1, 0, 0, 0, 1, 101, 1, 0, 0, 0, 1, 103, 1, 0, 0, 0, 1, 105, 1, 0, 0, 0, 1, 107, 1, 0, 0, 0, 1, 109, 1, 0, 0, 0, 1, 111, 1, 0, 0, 0, 1, 113, 1, 0, 0, 0, 1, 115, 1, 0, 0, 0, 1, 117, 1, 0, 0, 0, 1, 119, 1, 0, 0, 0, 1, 121, 1, 0, 0, 0, 1, 123, 1, 0, 0, 0, 1, 125, 1, 0, 0, 0, 1, 127, 1, 0, 0, 0, 1, 129, 1, 0, 0, 0, 1, 131, 1, 0, 0, 0, 1, 133, 1, 0, 0, 0, 1, 135, 1, 0, 0, 0, 1, 137, 1, 0, 0, 0, 1, 139, 1, 0, 0, 0, 1, 141, 1, 0, 0, 0, 1, 143, 1, 0, 0, 0, 1, 145, 1, 0, 0, 0, 1, 147, 1, 0, 0, 0, 1, 149, 1, 0, 0, 0, 1, 151, 1, 0, 0, 0, 1, 153, 1, 0, 0, 0, 1, 155, 1, 0, 0, 0, 1, 157, 1, 0, 0, 0, 1, 159, 1, 0, 0, 0, 1, 161, 1, 0, 0, 0, 1, 163, 1, 0, 0, 0, 1, 165, 1, 0, 0, 0, 1, 167, 1, 0, 0, 0, 1, 169, 1, 0, 0, 0, 1, 173, 1, 0, 0, 0, 1, 175, 1, 0, 0, 0, 1, 177, 1, 0, 0, 0, 1, 179, 1, 0, 0, 0, 2, 181, 1, 0, 0, 0, 2, 183, 1, 0, 0, 0, 2, 185, 1, 0, 0, 0, 2, 187, 1, 0, 0, 0, 2, 189, 1, 0, 0, 0, 3, 191, 1, 0, 0, 0, 3, 193, 1, 0, 0, 0, 3, 195, 1, 0, 0, 0, 3, 197, 1, 0, 0, 0, 3, 199, 1, 0, 0, 0, 3, 201, 1, 0, 0, 0, 3, 203, 1, 0, 0, 0, 3, 207, 1, 0, 0, 0, 3, 209, 1, 0, 0, 0, 3, 211, 1, 0, 0, 0, 3, 213, 1, 0, 0, 0, 3, 215, 1, 0, 0, 0, 3, 217, 1, 0, 0, 0, 4, 219, 1, 0, 0, 0, 4, 221, 1, 0, 0, 0, 4, 223, 1, 0, 0, 0, 4, 225, 1, 0, 0, 0, 4, 227, 1, 0, 0, 0, 4, 233, 1, 0, 0, 0, 4, 235, 1, 0, 0, 0, 4, 237, 1, 0, 0, 0, 4, 239, 1, 0, 0, 0, 5, 241, 1, 0, 0, 0, 5, 243, 1, 0, 0, 0, 5, 245, 1, 0, 0, 0, 5, 247, 1, 0, 0, 0, 5, 249, 1, 0, 0, 0, 5, 251, 1, 0, 0, 0, 5, 253, 1, 0, 0, 0, 5, 255, 1, 0, 0, 0, 5, 257, 1, 0, 0, 0, 5, 259, 1, 0, 0, 0, 5, 261, 1, 0, 0, 0, 6, 263, 1, 0, 0, 0, 6, 265, 1, 0, 0, 0, 6, 267, 1, 0, 0, 0, 6, 269, 1, 0, 0, 0, 6, 273, 1, 0, 0, 0, 6, 275, 1, 0, 0, 0, 6, 277, 1, 0, 0, 0, 6, 279, 1, 0, 0, 0, 6, 281, 1, 0, 0, 0, 7, 283, 1, 0, 0, 0, 7, 285, 1, 0, 0, 0, 7, 287, 1, 0, 0, 0, 7, 289, 1, 0, 0, 0, 7, 291, 1, 0, 0, 0, 7, 293, 1, 0, 0, 0, 7, 295, 1, 0, 0, 0, 7, 297, 1, 0, 0, 0, 7, 299, 1, 0, 0, 0, 7, 301, 1, 0, 0, 0, 7, 303, 1, 0, 0, 0, 7, 305, 1, 0, 0, 0, 8, 307, 1, 0, 0, 0, 8, 309, 1, 0, 0, 0, 8, 311, 1, 0, 0, 0, 8, 313, 1, 0, 0, 0, 8, 315, 1, 0, 0, 0, 8, 317, 1, 0, 0, 0, 8, 319, 1, 0, 0, 0, 8, 321, 1, 0, 0, 0, 8, 323, 1, 0, 0, 0, 9, 325, 1, 0, 0, 0, 9, 327, 1, 0, 0, 0, 9, 329, 1, 0, 0, 0, 9, 331, 1, 0, 0, 0, 9, 333, 1, 0, 0, 0, 10, 335, 1, 0, 0, 0, 10, 337, 1, 0, 0, 0, 10, 339, 1, 0, 0, 0, 10, 341, 1, 0, 0, 0, 10, 343, 1, 0, 0, 0, 10, 345, 1, 0, 0, 0, 11, 347, 1, 0, 0, 0, 11, 349, 1, 0, 0, 0, 11, 351, 1, 0, 0, 0, 11, 353, 1, 0, 0, 0, 11, 355, 1, 0, 0, 0, 11, 357, 1, 0, 0, 0, 11, 359, 1, 0, 0, 0, 11, 361, 1, 0, 0, 0, 11, 363, 1, 0, 0, 0, 11, 365, 1, 0, 0, 0, 12, 367, 1, 0, 0, 0, 12, 369, 1, 0, 0, 0, 12, 371, 1, 0, 0, 0, 12, 373, 1, 0, 0, 0, 12, 375, 1, 0, 0, 0, 12, 377, 1, 0, 0, 0, 12, 379, 1, 0, 0, 0, 13, 381, 1, 0, 0, 0, 13, 383, 1, 0, 0, 0, 13, 385, 1, 0, 0, 0, 13, 387, 1, 0, 0, 0, 13, 389, 1, 0, 0, 0, 13, 391, 1, 0, 0, 0, 14, 393, 1, 0, 0, 0, 14, 395, 1, 0, 0, 0, 14, 397, 1, 0, 0, 0, 14, 399, 1, 0, 0, 0, 14, 401, 1, 0, 0, 0, 14, 403, 1, 0, 0, 0, 14, 405, 1, 0, 0, 0, 14, 407, 1, 0, 0, 0, 14, 409, 1, 0, 0, 0, 15, 411, 1, 0, 0, 0, 17, 421, 1, 0, 0, 0, 19, 428, 1, 0, 0, 0, 21, 437, 1, 0, 0, 0, 23, 444, 1, 0, 0, 0, 25, 454, 1, 0, 0, 0, 27, 461, 1, 0, 0, 0, 29, 468, 1, 0, 0, 0, 31, 475, 1, 0, 0, 0, 33, 483, 1, 0, 0, 0, 35, 495, 1, 0, 0, 0, 37, 504, 1, 0, 0, 0, 39, 510, 1, 0, 0, 0, 41, 517, 1, 0, 0, 0, 43, 524, 1, 0, 0, 0, 45, 532, 1, 0, 0, 0, 47, 540, 1, 0, 0, 0, 49, 555, 1, 0, 0, 0, 51, 565, 1, 0, 0, 0, 53, 574, 1, 0, 0, 0, 55, 586, 1, 0, 0, 0, 57, 592, 1, 0, 0, 0, 59, 609, 1, 0, 0, 0, 61, 625, 1, 0, 0, 0, 63, 631, 1, 0, 0, 0, 65, 635, 1, 0, 0, 0, 67, 637, 1, 0, 0, 0, 69, 639, 1, 0, 0, 0, 71, 642, 1, 0, 0, 0, 73, 644, 1, 0, 0, 0, 75, 653, 1, 0, 0, 0, 77, 655, 1, 0, 0, 0, 79, 660, 1, 0, 0, 0, 81, 662, 1, 0, 0, 0, 83, 667, 1, 0, 0, 0, 85, 698, 1, 0, 0, 0, 87, 701, 1, 0, 0, 0, 89, 747, 1, 0, 0, 0, 91, 749, 1, 0, 0, 0, 93, 752, 1, 0, 0, 0, 95, 756, 1, 0, 0, 0, 97, 760, 1, 0, 0, 0, 99, 762, 1, 0, 0, 0, 101, 765, 1, 0, 0, 0, 103, 767, 1, 0, 0, 0, 105, 772, 1, 0, 0, 0, 107, 774, 1, 0, 0, 0, 109, 780, 1, 0, 0, 0, 111, 786, 1, 0, 0, 0, 113, 789, 1, 0, 0, 0, 115, 792, 1, 0, 0, 0, 117, 797, 1, 0, 0, 0, 119, 802, 1, 0, 0, 0, 121, 804, 1, 0, 0, 0, 123, 808, 1, 0, 0, 0, 125, 813, 1, 0, 0, 0, 127, 819, 1, 0, 0, 0, 129, 822, 1, 0, 0, 0, 131, 824, 1, 0, 0, 0, 133, 830, 1, 0, 0, 0, 135, 832, 1, 0, 0, 0, 137, 837, 1, 0, 0, 0, 139, 840, 1, 0, 0, 0, 141, 843, 1, 0, 0, 0, 143, 846, 1, 0, 0, 0, 145, 848, 1, 0, 0, 0, 147, 851, 1, 0, 0, 0, 149, 853, 1, 0, 0, 0, 151, 856, 1, 0, 0, 0, 153, 858, 1, 0, 0, 0, 155, 860, 1, 0, 0, 0, 157, 862, 1, 0, 0, 0, 159, 864, 1, 0, 0, 0, 161, 866, 1, 0, 0, 0, 163, 888, 1, 0, 0, 0, 165, 890, 1, 0, 0, 0, 167, 895, 1, 0, 0, 0, 169, 916, 1, 0, 0, 0, 171, 918, 1, 0, 0, 0, 173, 926, 1, 0, 0, 0, 175, 928, 1, 0, 0, 0, 177, 932, 1, 0, 0, 0, 179, 936, 1, 0, 0, 0, 181, 940, 1, 0, 0, 0, 183, 945, 1, 0, 0, 0, 185, 950, 1, 0, 0, 0, 187, 954, 1, 0, 0, 0, 189, 958, 1, 0, 0, 0, 191, 962, 1, 0, 0, 0, 193, 967, 1, 0, 0, 0, 195, 971, 1, 0, 0, 0, 197, 975, 1, 0, 0, 0, 199, 979, 1, 0, 0, 0, 201, 983, 1, 0, 0, 0, 203, 987, 1, 0, 0, 0, 205, 999, 1, 0, 0, 0, 207, 1002, 1, 0, 0, 0, 209, 1006, 1, 0, 0, 0, 211, 1010, 1, 0, 0, 0, 213, 1014, 1, 0, 0, 0, 215, 1018, 1, 0, 0, 0, 217, 1022, 1, 0, 0, 0, 219, 1026, 1, 0, 0, 0, 221, 1031, 1, 0, 0, 0, 223, 1035, 1, 0, 0, 0, 225, 1039, 1, 0, 0, 0, 227, 1043, 1, 0, 0, 0, 229, 1051, 1, 0, 0, 0, 231, 1072, 1, 0, 0, 0, 233, 1076, 1, 0, 0, 0, 235, 1080, 1, 0, 0, 0, 237, 1084, 1, 0, 0, 0, 239, 1088, 1, 0, 0, 0, 241, 1092, 1, 0, 0, 0, 243, 1097, 1, 0, 0, 0, 245, 1101, 1, 0, 0, 0, 247, 1105, 1, 0, 0, 0, 249, 1109, 1, 0, 0, 0, 251, 1113, 1, 0, 0, 0, 253, 1117, 1, 0, 0, 0, 255, 1120, 1, 0, 0, 0, 257, 1124, 1, 0, 0, 0, 259, 1128, 1, 0, 0, 0, 261, 1132, 1, 0, 0, 0, 263, 1136, 1, 0, 0, 0, 265, 1141, 1, 0, 0, 0, 267, 1146, 1, 0, 0, 0, 269, 1151, 1, 0, 0, 0, 271, 1158, 1, 0, 0, 0, 273, 1167, 1, 0, 0, 0, 275, 1174, 1, 0, 0, 0, 277, 1178, 1, 0, 0, 0, 279, 1182, 1, 0, 0, 0, 281, 1186, 1, 0, 0, 0, 283, 1190, 1, 0, 0, 0, 285, 1196, 1, 0, 0, 0, 287, 1200, 1, 0, 0, 0, 289, 1204, 1, 0, 0, 0, 291, 1208, 1, 0, 0, 0, 293, 1212, 1, 0, 0, 0, 295, 1216, 1, 0, 0, 0, 297, 1220, 1, 0, 0, 0, 299, 1224, 1, 0, 0, 0, 301, 1228, 1, 0, 0, 0, 303, 1232, 1, 0, 0, 0, 305, 1236, 1, 0, 0, 0, 307, 1240, 1, 0, 0, 0, 309, 1245, 1, 0, 0, 0, 311, 1249, 1, 0, 0, 0, 313, 1253, 1, 0, 0, 0, 315, 1257, 1, 0, 0, 0, 317, 1261, 1, 0, 0, 0, 319, 1265, 1, 0, 0, 0, 321, 1269, 1, 0, 0, 0, 323, 1273, 1, 0, 0, 0, 325, 1277, 1, 0, 0, 0, 327, 1282, 1, 0, 0, 0, 329, 1287, 1, 0, 0, 0, 331, 1291, 1, 0, 0, 0, 333, 1295, 1, 0, 0, 0, 335, 1299, 1, 0, 0, 0, 337, 1304, 1, 0, 0, 0, 339, 1311, 1, 0, 0, 0, 341, 1315, 1, 0, 0, 0, 343, 1319, 1, 0, 0, 0, 345, 1323, 1, 0, 0, 0, 347, 1327, 1, 0, 0, 0, 349, 1332, 1, 0, 0, 0, 351, 1336, 1, 0, 0, 0, 353, 1340, 1, 0, 0, 0, 355, 1344, 1, 0, 0, 0, 357, 1349, 1, 0, 0, 0, 359, 1353, 1, 0, 0, 0, 361, 1357, 1, 0, 0, 0, 363, 1361, 1, 0, 0, 0, 365, 1365, 1, 0, 0, 0, 367, 1369, 1, 0, 0, 0, 369, 1375, 1, 0, 0, 0, 371, 1379, 1, 0, 0, 0, 373, 1383, 1, 0, 0, 0, 375, 1387, 1, 0, 0, 0, 377, 1391, 1, 0, 0, 0, 379, 1395, 1, 0, 0, 0, 381, 1399, 1, 0, 0, 0, 383, 1404, 1, 0, 0, 0, 385, 1410, 1, 0, 0, 0, 387, 1416, 1, 0, 0, 0, 389, 1420, 1, 0, 0, 0, 391, 1424, 1, 0, 0, 0, 393, 1428, 1, 0, 0, 0, 395, 1434, 1, 0, 0, 0, 397, 1440, 1, 0, 0, 0, 399, 1444, 1, 0, 0, 0, 401, 1448, 1, 0, 0, 0, 403, 1452, 1, 0, 0, 0, 405, 1458, 1, 0, 0, 0, 407, 1464, 1, 0, 0, 0, 409, 1470, 1, 0, 0, 0, 411, 412, 7, 0, 0, 0, 412, 413, 7, 1, 0, 0, 413, 414, 7, 2, 0, 0, 414, 415, 7, 2, 0, 0, 415, 416, 7, 3, 0, 0, 416, 417, 7, 4, 0, 0, 417, 418, 7, 5, 0, 0, 418, 419, 1, 0, 0, 0, 419, 420, 6, 0, 0, 0, 420, 16, 1, 0, 0, 0, 421, 422, 7, 0, 0, 0, 422, 423, 7, 6, 0, 0, 423, 424, 7, 7, 0, 0, 424, 425, 7, 8, 0, 0, 425, 426, 1, 0, 0, 0, 426, 427, 6, 1, 1, 0, 427, 18, 1, 0, 0, 0, 428, 429, 7, 3, 0, 0, 429, 430, 7, 9, 0, 0, 430, 431, 7, 6, 0, 0, 431, 432, 7, 1, 0, 0, 432, 433, 7, 4, 0, 0, 433, 434, 7, 10, 0, 0, 434, 435, 1, 0, 0, 0, 435, 436, 6, 2, 2, 0, 436, 20, 1, 0, 0, 0, 437, 438, 7, 3, 0, 0, 438, 439, 7, 11, 0, 0, 439, 440, 7, 12, 0, 0, 440, 441, 7, 13, 0, 0, 441, 442, 1, 0, 0, 0, 442, 443, 6, 3, 0, 0, 443, 22, 1, 0, 0, 0, 444, 445, 7, 3, 0, 0, 445, 446, 7, 14, 0, 0, 446, 447, 7, 8, 0, 0, 447, 448, 7, 13, 0, 0, 448, 449, 7, 12, 0, 0, 449, 450, 7, 1, 0, 0, 450, 451, 7, 9, 0, 0, 451, 452, 1, 0, 0, 0, 452, 453, 6, 4, 3, 0, 453, 24, 1, 0, 0, 0, 454, 455, 7, 15, 0, 0, 455, 456, 7, 6, 0, 0, 456, 457, 7, 7, 0, 0, 457, 458, 7, 16, 0, 0, 458, 459, 1, 0, 0, 0, 459, 460, 6, 5, 4, 0, 460, 26, 1, 0, 0, 0, 461, 462, 7, 17, 0, 0, 462, 463, 7, 6, 0, 0, 463, 464, 7, 7, 0, 0, 464, 465, 7, 18, 0, 0, 465, 466, 1, 0, 0, 0, 466, 467, 6, 6, 0, 0, 467, 28, 1, 0, 0, 0, 468, 469, 7, 18, 0, 0, 469, 470, 7, 3, 0, 0, 470, 471, 7, 3, 0, 0, 471, 472, 7, 8, 0, 0, 472, 473, 1, 0, 0, 0, 473, 474, 6, 7, 1, 0, 474, 30, 1, 0, 0, 0, 475, 476, 7, 13, 0, 0, 476, 477, 7, 1, 0, 0, 477, 478, 7, 16, 0, 0, 478, 479, 7, 1, 0, 0, 479, 480, 7, 5, 0, 0, 480, 481, 1, 0, 0, 0, 481, 482, 6, 8, 0, 0, 482, 32, 1, 0, 0, 0, 483, 484, 7, 16, 0, 0, 484, 485, 7, 11, 0, 0, 485, 486, 5, 95, 0, 0, 486, 487, 7, 3, 0, 0, 487, 488, 7, 14, 0, 0, 488, 489, 7, 8, 0, 0, 489, 490, 7, 12, 0, 0, 490, 491, 7, 9, 0, 0, 491, 492, 7, 0, 0, 0, 492, 493, 1, 0, 0, 0, 493, 494, 6, 9, 5, 0, 494, 34, 1, 0, 0, 0, 495, 496, 7, 6, 0, 0, 496, 497, 7, 3, 0, 0, 497, 498, 7, 9, 0, 0, 498, 499, 7, 12, 0, 0, 499, 500, 7, 16, 0, 0, 500, 501, 7, 3, 0, 0, 501, 502, 1, 0, 0, 0, 502, 503, 6, 10, 6, 0, 503, 36, 1, 0, 0, 0, 504, 505, 7, 6, 0, 0, 505, 506, 7, 7, 0, 0, 506, 507, 7, 19, 0, 0, 507, 508, 1, 0, 0, 0, 508, 509, 6, 11, 0, 0, 509, 38, 1, 0, 0, 0, 510, 511, 7, 2, 0, 0, 511, 512, 7, 10, 0, 0, 512, 513, 7, 7, 0, 0, 513, 514, 7, 19, 0, 0, 514, 515, 1, 0, 0, 0, 515, 516, 6, 12, 7, 0, 516, 40, 1, 0, 0, 0, 517, 518, 7, 2, 0, 0, 518, 519, 7, 7, 0, 0, 519, 520, 7, 6, 0, 0, 520, 521, 7, 5, 0, 0, 521, 522, 1, 0, 0, 0, 522, 523, 6, 13, 0, 0, 523, 42, 1, 0, 0, 0, 524, 525, 7, 2, 0, 0, 525, 526, 7, 5, 0, 0, 526, 527, 7, 12, 0, 0, 527, 528, 7, 5, 0, 0, 528, 529, 7, 2, 0, 0, 529, 530, 1, 0, 0, 0, 530, 531, 6, 14, 0, 0, 531, 44, 1, 0, 0, 0, 532, 533, 7, 19, 0, 0, 533, 534, 7, 10, 0, 0, 534, 535, 7, 3, 0, 0, 535, 536, 7, 6, 0, 0, 536, 537, 7, 3, 0, 0, 537, 538, 1, 0, 0, 0, 538, 539, 6, 15, 0, 0, 539, 46, 1, 0, 0, 0, 540, 541, 4, 16, 0, 0, 541, 542, 7, 1, 0, 0, 542, 543, 7, 9, 0, 0, 543, 544, 7, 13, 0, 0, 544, 545, 7, 1, 0, 0, 545, 546, 7, 9, 0, 0, 546, 547, 7, 3, 0, 0, 547, 548, 7, 2, 0, 0, 548, 549, 7, 5, 0, 0, 549, 550, 7, 12, 0, 0, 550, 551, 7, 5, 0, 0, 551, 552, 7, 2, 0, 0, 552, 553, 1, 0, 0, 0, 553, 554, 6, 16, 0, 0, 554, 48, 1, 0, 0, 0, 555, 556, 4, 17, 1, 0, 556, 557, 7, 13, 0, 0, 557, 558, 7, 7, 0, 0, 558, 559, 7, 7, 0, 0, 559, 560, 7, 18, 0, 0, 560, 561, 7, 20, 0, 0, 561, 562, 7, 8, 0, 0, 562, 563, 1, 0, 0, 0, 563, 564, 6, 17, 8, 0, 564, 50, 1, 0, 0, 0, 565, 566, 4, 18, 2, 0, 566, 567, 7, 16, 0, 0, 567, 568, 7, 12, 0, 0, 568, 569, 7, 5, 0, 0, 569, 570, 7, 4, 0, 0, 570, 571, 7, 10, 0, 0, 571, 572, 1, 0, 0, 0, 572, 573, 6, 18, 0, 0, 573, 52, 1, 0, 0, 0, 574, 575, 4, 19, 3, 0, 575, 576, 7, 16, 0, 0, 576, 577, 7, 3, 0, 0, 577, 578, 7, 5, 0, 0, 578, 579, 7, 6, 0, 0, 579, 580, 7, 1, 0, 0, 580, 581, 7, 4, 0, 0, 581, 582, 7, 2, 0, 0, 582, 583, 1, 0, 0, 0, 583, 584, 6, 19, 9, 0, 584, 54, 1, 0, 0, 0, 585, 587, 8, 21, 0, 0, 586, 585, 1, 0, 0, 0, 587, 588, 1, 0, 0, 0, 588, 586, 1, 0, 0, 0, 588, 589, 1, 0, 0, 0, 589, 590, 1, 0, 0, 0, 590, 591, 6, 20, 0, 0, 591, 56, 1, 0, 0, 0, 592, 593, 5, 47, 0, 0, 593, 594, 5, 47, 0, 0, 594, 598, 1, 0, 0, 0, 595, 597, 8, 22, 0, 0, 596, 595, 1, 0, 0, 0, 597, 600, 1, 0, 0, 0, 598, 596, 1, 0, 0, 0, 598, 599, 1, 0, 0, 0, 599, 602, 1, 0, 0, 0, 600, 598, 1, 0, 0, 0, 601, 603, 5, 13, 0, 0, 602, 601, 1, 0, 0, 0, 602, 603, 1, 0, 0, 0, 603, 605, 1, 0, 0, 0, 604, 606, 5, 10, 0, 0, 605, 604, 1, 0, 0, 0, 605, 606, 1, 0, 0, 0, 606, 607, 1, 0, 0, 0, 607, 608, 6, 21, 10, 0, 608, 58, 1, 0, 0, 0, 609, 610, 5, 47, 0, 0, 610, 611, 5, 42, 0, 0, 611, 616, 1, 0, 0, 0, 612, 615, 3, 59, 22, 0, 613, 615, 9, 0, 0, 0, 614, 612, 1, 0, 0, 0, 614, 613, 1, 0, 0, 0, 615, 618, 1, 0, 0, 0, 616, 617, 1, 0, 0, 0, 616, 614, 1, 0, 0, 0, 617, 619, 1, 0, 0, 0, 618, 616, 1, 0, 0, 0, 619, 620, 5, 42, 0, 0, 620, 621, 5, 47, 0, 0, 621, 622, 1, 0, 0, 0, 622, 623, 6, 22, 10, 0, 623, 60, 1, 0, 0, 0, 624, 626, 7, 23, 0, 0, 625, 624, 1, 0, 0, 0, 626, 627, 1, 0, 0, 0, 627, 625, 1, 0, 0, 0, 627, 628, 1, 0, 0, 0, 628, 629, 1, 0, 0, 0, 629, 630, 6, 23, 10, 0, 630, 62, 1, 0, 0, 0, 631, 632, 5, 124, 0, 0, 632, 633, 1, 0, 0, 0, 633, 634, 6, 24, 11, 0, 634, 64, 1, 0, 0, 0, 635, 636, 7, 24, 0, 0, 636, 66, 1, 0, 0, 0, 637, 638, 7, 25, 0, 0, 638, 68, 1, 0, 0, 0, 639, 640, 5, 92, 0, 0, 640, 641, 7, 26, 0, 0, 641, 70, 1, 0, 0, 0, 642, 643, 8, 27, 0, 0, 643, 72, 1, 0, 0, 0, 644, 646, 7, 3, 0, 0, 645, 647, 7, 28, 0, 0, 646, 645, 1, 0, 0, 0, 646, 647, 1, 0, 0, 0, 647, 649, 1, 0, 0, 0, 648, 650, 3, 65, 25, 0, 649, 648, 1, 0, 0, 0, 650, 651, 1, 0, 0, 0, 651, 649, 1, 0, 0, 0, 651, 652, 1, 0, 0, 0, 652, 74, 1, 0, 0, 0, 653, 654, 5, 64, 0, 0, 654, 76, 1, 0, 0, 0, 655, 656, 5, 96, 0, 0, 656, 78, 1, 0, 0, 0, 657, 661, 8, 29, 0, 0, 658, 659, 5, 96, 0, 0, 659, 661, 5, 96, 0, 0, 660, 657, 1, 0, 0, 0, 660, 658, 1, 0, 0, 0, 661, 80, 1, 0, 0, 0, 662, 663, 5, 95, 0, 0, 663, 82, 1, 0, 0, 0, 664, 668, 3, 67, 26, 0, 665, 668, 3, 65, 25, 0, 666, 668, 3, 81, 33, 0, 667, 664, 1, 0, 0, 0, 667, 665, 1, 0, 0, 0, 667, 666, 1, 0, 0, 0, 668, 84, 1, 0, 0, 0, 669, 674, 5, 34, 0, 0, 670, 673, 3, 69, 27, 0, 671, 673, 3, 71, 28, 0, 672, 670, 1, 0, 0, 0, 672, 671, 1, 0, 0, 0, 673, 676, 1, 0, 0, 0, 674, 672, 1, 0, 0, 0, 674, 675, 1, 0, 0, 0, 675, 677, 1, 0, 0, 0, 676, 674, 1, 0, 0, 0, 677, 699, 5, 34, 0, 0, 678, 679, 5, 34, 0, 0, 679, 680, 5, 34, 0, 0, 680, 681, 5, 34, 0, 0, 681, 685, 1, 0, 0, 0, 682, 684, 8, 22, 0, 0, 683, 682, 1, 0, 0, 0, 684, 687, 1, 0, 0, 0, 685, 686, 1, 0, 0, 0, 685, 683, 1, 0, 0, 0, 686, 688, 1, 0, 0, 0, 687, 685, 1, 0, 0, 0, 688, 689, 5, 34, 0, 0, 689, 690, 5, 34, 0, 0, 690, 691, 5, 34, 0, 0, 691, 693, 1, 0, 0, 0, 692, 694, 5, 34, 0, 0, 693, 692, 1, 0, 0, 0, 693, 694, 1, 0, 0, 0, 694, 696, 1, 0, 0, 0, 695, 697, 5, 34, 0, 0, 696, 695, 1, 0, 0, 0, 696, 697, 1, 0, 0, 0, 697, 699, 1, 0, 0, 0, 698, 669, 1, 0, 0, 0, 698, 678, 1, 0, 0, 0, 699, 86, 1, 0, 0, 0, 700, 702, 3, 65, 25, 0, 701, 700, 1, 0, 0, 0, 702, 703, 1, 0, 0, 0, 703, 701, 1, 0, 0, 0, 703, 704, 1, 0, 0, 0, 704, 88, 1, 0, 0, 0, 705, 707, 3, 65, 25, 0, 706, 705, 1, 0, 0, 0, 707, 708, 1, 0, 0, 0, 708, 706, 1, 0, 0, 0, 708, 709, 1, 0, 0, 0, 709, 710, 1, 0, 0, 0, 710, 714, 3, 105, 45, 0, 711, 713, 3, 65, 25, 0, 712, 711, 1, 0, 0, 0, 713, 716, 1, 0, 0, 0, 714, 712, 1, 0, 0, 0, 714, 715, 1, 0, 0, 0, 715, 748, 1, 0, 0, 0, 716, 714, 1, 0, 0, 0, 717, 719, 3, 105, 45, 0, 718, 720, 3, 65, 25, 0, 719, 718, 1, 0, 0, 0, 720, 721, 1, 0, 0, 0, 721, 719, 1, 0, 0, 0, 721, 722, 1, 0, 0, 0, 722, 748, 1, 0, 0, 0, 723, 725, 3, 65, 25, 0, 724, 723, 1, 0, 0, 0, 725, 726, 1, 0, 0, 0, 726, 724, 1, 0, 0, 0, 726, 727, 1, 0, 0, 0, 727, 735, 1, 0, 0, 0, 728, 732, 3, 105, 45, 0, 729, 731, 3, 65, 25, 0, 730, 729, 1, 0, 0, 0, 731, 734, 1, 0, 0, 0, 732, 730, 1, 0, 0, 0, 732, 733, 1, 0, 0, 0, 733, 736, 1, 0, 0, 0, 734, 732, 1, 0, 0, 0, 735, 728, 1, 0, 0, 0, 735, 736, 1, 0, 0, 0, 736, 737, 1, 0, 0, 0, 737, 738, 3, 73, 29, 0, 738, 748, 1, 0, 0, 0, 739, 741, 3, 105, 45, 0, 740, 742, 3, 65, 25, 0, 741, 740, 1, 0, 0, 0, 742, 743, 1, 0, 0, 0, 743, 741, 1, 0, 0, 0, 743, 744, 1, 0, 0, 0, 744, 745, 1, 0, 0, 0, 745, 746, 3, 73, 29, 0, 746, 748, 1, 0, 0, 0, 747, 706, 1, 0, 0, 0, 747, 717, 1, 0, 0, 0, 747, 724, 1, 0, 0, 0, 747, 739, 1, 0, 0, 0, 748, 90, 1, 0, 0, 0, 749, 750, 7, 30, 0, 0, 750, 751, 7, 31, 0, 0, 751, 92, 1, 0, 0, 0, 752, 753, 7, 12, 0, 0, 753, 754, 7, 9, 0, 0, 754, 755, 7, 0, 0, 0, 755, 94, 1, 0, 0, 0, 756, 757, 7, 12, 0, 0, 757, 758, 7, 2, 0, 0, 758, 759, 7, 4, 0, 0, 759, 96, 1, 0, 0, 0, 760, 761, 5, 61, 0, 0, 761, 98, 1, 0, 0, 0, 762, 763, 5, 58, 0, 0, 763, 764, 5, 58, 0, 0, 764, 100, 1, 0, 0, 0, 765, 766, 5, 44, 0, 0, 766, 102, 1, 0, 0, 0, 767, 768, 7, 0, 0, 0, 768, 769, 7, 3, 0, 0, 769, 770, 7, 2, 0, 0, 770, 771, 7, 4, 0, 0, 771, 104, 1, 0, 0, 0, 772, 773, 5, 46, 0, 0, 773, 106, 1, 0, 0, 0, 774, 775, 7, 15, 0, 0, 775, 776, 7, 12, 0, 0, 776, 777, 7, 13, 0, 0, 777, 778, 7, 2, 0, 0, 778, 779, 7, 3, 0, 0, 779, 108, 1, 0, 0, 0, 780, 781, 7, 15, 0, 0, 781, 782, 7, 1, 0, 0, 782, 783, 7, 6, 0, 0, 783, 784, 7, 2, 0, 0, 784, 785, 7, 5, 0, 0, 785, 110, 1, 0, 0, 0, 786, 787, 7, 1, 0, 0, 787, 788, 7, 9, 0, 0, 788, 112, 1, 0, 0, 0, 789, 790, 7, 1, 0, 0, 790, 791, 7, 2, 0, 0, 791, 114, 1, 0, 0, 0, 792, 793, 7, 13, 0, 0, 793, 794, 7, 12, 0, 0, 794, 795, 7, 2, 0, 0, 795, 796, 7, 5, 0, 0, 796, 116, 1, 0, 0, 0, 797, 798, 7, 13, 0, 0, 798, 799, 7, 1, 0, 0, 799, 800, 7, 18, 0, 0, 800, 801, 7, 3, 0, 0, 801, 118, 1, 0, 0, 0, 802, 803, 5, 40, 0, 0, 803, 120, 1, 0, 0, 0, 804, 805, 7, 9, 0, 0, 805, 806, 7, 7, 0, 0, 806, 807, 7, 5, 0, 0, 807, 122, 1, 0, 0, 0, 808, 809, 7, 9, 0, 0, 809, 810, 7, 20, 0, 0, 810, 811, 7, 13, 0, 0, 811, 812, 7, 13, 0, 0, 812, 124, 1, 0, 0, 0, 813, 814, 7, 9, 0, 0, 814, 815, 7, 20, 0, 0, 815, 816, 7, 13, 0, 0, 816, 817, 7, 13, 0, 0, 817, 818, 7, 2, 0, 0, 818, 126, 1, 0, 0, 0, 819, 820, 7, 7, 0, 0, 820, 821, 7, 6, 0, 0, 821, 128, 1, 0, 0, 0, 822, 823, 5, 63, 0, 0, 823, 130, 1, 0, 0, 0, 824, 825, 7, 6, 0, 0, 825, 826, 7, 13, 0, 0, 826, 827, 7, 1, 0, 0, 827, 828, 7, 18, 0, 0, 828, 829, 7, 3, 0, 0, 829, 132, 1, 0, 0, 0, 830, 831, 5, 41, 0, 0, 831, 134, 1, 0, 0, 0, 832, 833, 7, 5, 0, 0, 833, 834, 7, 6, 0, 0, 834, 835, 7, 20, 0, 0, 835, 836, 7, 3, 0, 0, 836, 136, 1, 0, 0, 0, 837, 838, 5, 61, 0, 0, 838, 839, 5, 61, 0, 0, 839, 138, 1, 0, 0, 0, 840, 841, 5, 61, 0, 0, 841, 842, 5, 126, 0, 0, 842, 140, 1, 0, 0, 0, 843, 844, 5, 33, 0, 0, 844, 845, 5, 61, 0, 0, 845, 142, 1, 0, 0, 0, 846, 847, 5, 60, 0, 0, 847, 144, 1, 0, 0, 0, 848, 849, 5, 60, 0, 0, 849, 850, 5, 61, 0, 0, 850, 146, 1, 0, 0, 0, 851, 852, 5, 62, 0, 0, 852, 148, 1, 0, 0, 0, 853, 854, 5, 62, 0, 0, 854, 855, 5, 61, 0, 0, 855, 150, 1, 0, 0, 0, 856, 857, 5, 43, 0, 0, 857, 152, 1, 0, 0, 0, 858, 859, 5, 45, 0, 0, 859, 154, 1, 0, 0, 0, 860, 861, 5, 42, 0, 0, 861, 156, 1, 0, 0, 0, 862, 863, 5, 47, 0, 0, 863, 158, 1, 0, 0, 0, 864, 865, 5, 37, 0, 0, 865, 160, 1, 0, 0, 0, 866, 867, 4, 73, 4, 0, 867, 868, 3, 51, 18, 0, 868, 869, 1, 0, 0, 0, 869, 870, 6, 73, 12, 0, 870, 162, 1, 0, 0, 0, 871, 874, 3, 129, 57, 0, 872, 875, 3, 67, 26, 0, 873, 875, 3, 81, 33, 0, 874, 872, 1, 0, 0, 0, 874, 873, 1, 0, 0, 0, 875, 879, 1, 0, 0, 0, 876, 878, 3, 83, 34, 0, 877, 876, 1, 0, 0, 0, 878, 881, 1, 0, 0, 0, 879, 877, 1, 0, 0, 0, 879, 880, 1, 0, 0, 0, 880, 889, 1, 0, 0, 0, 881, 879, 1, 0, 0, 0, 882, 884, 3, 129, 57, 0, 883, 885, 3, 65, 25, 0, 884, 883, 1, 0, 0, 0, 885, 886, 1, 0, 0, 0, 886, 884, 1, 0, 0, 0, 886, 887, 1, 0, 0, 0, 887, 889, 1, 0, 0, 0, 888, 871, 1, 0, 0, 0, 888, 882, 1, 0, 0, 0, 889, 164, 1, 0, 0, 0, 890, 891, 5, 91, 0, 0, 891, 892, 1, 0, 0, 0, 892, 893, 6, 75, 0, 0, 893, 894, 6, 75, 0, 0, 894, 166, 1, 0, 0, 0, 895, 896, 5, 93, 0, 0, 896, 897, 1, 0, 0, 0, 897, 898, 6, 76, 11, 0, 898, 899, 6, 76, 11, 0, 899, 168, 1, 0, 0, 0, 900, 904, 3, 67, 26, 0, 901, 903, 3, 83, 34, 0, 902, 901, 1, 0, 0, 0, 903, 906, 1, 0, 0, 0, 904, 902, 1, 0, 0, 0, 904, 905, 1, 0, 0, 0, 905, 917, 1, 0, 0, 0, 906, 904, 1, 0, 0, 0, 907, 910, 3, 81, 33, 0, 908, 910, 3, 75, 30, 0, 909, 907, 1, 0, 0, 0, 909, 908, 1, 0, 0, 0, 910, 912, 1, 0, 0, 0, 911, 913, 3, 83, 34, 0, 912, 911, 1, 0, 0, 0, 913, 914, 1, 0, 0, 0, 914, 912, 1, 0, 0, 0, 914, 915, 1, 0, 0, 0, 915, 917, 1, 0, 0, 0, 916, 900, 1, 0, 0, 0, 916, 909, 1, 0, 0, 0, 917, 170, 1, 0, 0, 0, 918, 920, 3, 77, 31, 0, 919, 921, 3, 79, 32, 0, 920, 919, 1, 0, 0, 0, 921, 922, 1, 0, 0, 0, 922, 920, 1, 0, 0, 0, 922, 923, 1, 0, 0, 0, 923, 924, 1, 0, 0, 0, 924, 925, 3, 77, 31, 0, 925, 172, 1, 0, 0, 0, 926, 927, 3, 171, 78, 0, 927, 174, 1, 0, 0, 0, 928, 929, 3, 57, 21, 0, 929, 930, 1, 0, 0, 0, 930, 931, 6, 80, 10, 0, 931, 176, 1, 0, 0, 0, 932, 933, 3, 59, 22, 0, 933, 934, 1, 0, 0, 0, 934, 935, 6, 81, 10, 0, 935, 178, 1, 0, 0, 0, 936, 937, 3, 61, 23, 0, 937, 938, 1, 0, 0, 0, 938, 939, 6, 82, 10, 0, 939, 180, 1, 0, 0, 0, 940, 941, 3, 165, 75, 0, 941, 942, 1, 0, 0, 0, 942, 943, 6, 83, 13, 0, 943, 944, 6, 83, 14, 0, 944, 182, 1, 0, 0, 0, 945, 946, 3, 63, 24, 0, 946, 947, 1, 0, 0, 0, 947, 948, 6, 84, 15, 0, 948, 949, 6, 84, 11, 0, 949, 184, 1, 0, 0, 0, 950, 951, 3, 61, 23, 0, 951, 952, 1, 0, 0, 0, 952, 953, 6, 85, 10, 0, 953, 186, 1, 0, 0, 0, 954, 955, 3, 57, 21, 0, 955, 956, 1, 0, 0, 0, 956, 957, 6, 86, 10, 0, 957, 188, 1, 0, 0, 0, 958, 959, 3, 59, 22, 0, 959, 960, 1, 0, 0, 0, 960, 961, 6, 87, 10, 0, 961, 190, 1, 0, 0, 0, 962, 963, 3, 63, 24, 0, 963, 964, 1, 0, 0, 0, 964, 965, 6, 88, 15, 0, 965, 966, 6, 88, 11, 0, 966, 192, 1, 0, 0, 0, 967, 968, 3, 165, 75, 0, 968, 969, 1, 0, 0, 0, 969, 970, 6, 89, 13, 0, 970, 194, 1, 0, 0, 0, 971, 972, 3, 167, 76, 0, 972, 973, 1, 0, 0, 0, 973, 974, 6, 90, 16, 0, 974, 196, 1, 0, 0, 0, 975, 976, 3, 337, 161, 0, 976, 977, 1, 0, 0, 0, 977, 978, 6, 91, 17, 0, 978, 198, 1, 0, 0, 0, 979, 980, 3, 101, 43, 0, 980, 981, 1, 0, 0, 0, 981, 982, 6, 92, 18, 0, 982, 200, 1, 0, 0, 0, 983, 984, 3, 97, 41, 0, 984, 985, 1, 0, 0, 0, 985, 986, 6, 93, 19, 0, 986, 202, 1, 0, 0, 0, 987, 988, 7, 16, 0, 0, 988, 989, 7, 3, 0, 0, 989, 990, 7, 5, 0, 0, 990, 991, 7, 12, 0, 0, 991, 992, 7, 0, 0, 0, 992, 993, 7, 12, 0, 0, 993, 994, 7, 5, 0, 0, 994, 995, 7, 12, 0, 0, 995, 204, 1, 0, 0, 0, 996, 1000, 8, 32, 0, 0, 997, 998, 5, 47, 0, 0, 998, 1000, 8, 33, 0, 0, 999, 996, 1, 0, 0, 0, 999, 997, 1, 0, 0, 0, 1000, 206, 1, 0, 0, 0, 1001, 1003, 3, 205, 95, 0, 1002, 1001, 1, 0, 0, 0, 1003, 1004, 1, 0, 0, 0, 1004, 1002, 1, 0, 0, 0, 1004, 1005, 1, 0, 0, 0, 1005, 208, 1, 0, 0, 0, 1006, 1007, 3, 207, 96, 0, 1007, 1008, 1, 0, 0, 0, 1008, 1009, 6, 97, 20, 0, 1009, 210, 1, 0, 0, 0, 1010, 1011, 3, 85, 35, 0, 1011, 1012, 1, 0, 0, 0, 1012, 1013, 6, 98, 21, 0, 1013, 212, 1, 0, 0, 0, 1014, 1015, 3, 57, 21, 0, 1015, 1016, 1, 0, 0, 0, 1016, 1017, 6, 99, 10, 0, 1017, 214, 1, 0, 0, 0, 1018, 1019, 3, 59, 22, 0, 1019, 1020, 1, 0, 0, 0, 1020, 1021, 6, 100, 10, 0, 1021, 216, 1, 0, 0, 0, 1022, 1023, 3, 61, 23, 0, 1023, 1024, 1, 0, 0, 0, 1024, 1025, 6, 101, 10, 0, 1025, 218, 1, 0, 0, 0, 1026, 1027, 3, 63, 24, 0, 1027, 1028, 1, 0, 0, 0, 1028, 1029, 6, 102, 15, 0, 1029, 1030, 6, 102, 11, 0, 1030, 220, 1, 0, 0, 0, 1031, 1032, 3, 105, 45, 0, 1032, 1033, 1, 0, 0, 0, 1033, 1034, 6, 103, 22, 0, 1034, 222, 1, 0, 0, 0, 1035, 1036, 3, 101, 43, 0, 1036, 1037, 1, 0, 0, 0, 1037, 1038, 6, 104, 18, 0, 1038, 224, 1, 0, 0, 0, 1039, 1040, 3, 129, 57, 0, 1040, 1041, 1, 0, 0, 0, 1041, 1042, 6, 105, 23, 0, 1042, 226, 1, 0, 0, 0, 1043, 1044, 3, 163, 74, 0, 1044, 1045, 1, 0, 0, 0, 1045, 1046, 6, 106, 24, 0, 1046, 228, 1, 0, 0, 0, 1047, 1052, 3, 67, 26, 0, 1048, 1052, 3, 65, 25, 0, 1049, 1052, 3, 81, 33, 0, 1050, 1052, 3, 155, 70, 0, 1051, 1047, 1, 0, 0, 0, 1051, 1048, 1, 0, 0, 0, 1051, 1049, 1, 0, 0, 0, 1051, 1050, 1, 0, 0, 0, 1052, 230, 1, 0, 0, 0, 1053, 1056, 3, 67, 26, 0, 1054, 1056, 3, 155, 70, 0, 1055, 1053, 1, 0, 0, 0, 1055, 1054, 1, 0, 0, 0, 1056, 1060, 1, 0, 0, 0, 1057, 1059, 3, 229, 107, 0, 1058, 1057, 1, 0, 0, 0, 1059, 1062, 1, 0, 0, 0, 1060, 1058, 1, 0, 0, 0, 1060, 1061, 1, 0, 0, 0, 1061, 1073, 1, 0, 0, 0, 1062, 1060, 1, 0, 0, 0, 1063, 1066, 3, 81, 33, 0, 1064, 1066, 3, 75, 30, 0, 1065, 1063, 1, 0, 0, 0, 1065, 1064, 1, 0, 0, 0, 1066, 1068, 1, 0, 0, 0, 1067, 1069, 3, 229, 107, 0, 1068, 1067, 1, 0, 0, 0, 1069, 1070, 1, 0, 0, 0, 1070, 1068, 1, 0, 0, 0, 1070, 1071, 1, 0, 0, 0, 1071, 1073, 1, 0, 0, 0, 1072, 1055, 1, 0, 0, 0, 1072, 1065, 1, 0, 0, 0, 1073, 232, 1, 0, 0, 0, 1074, 1077, 3, 231, 108, 0, 1075, 1077, 3, 171, 78, 0, 1076, 1074, 1, 0, 0, 0, 1076, 1075, 1, 0, 0, 0, 1077, 1078, 1, 0, 0, 0, 1078, 1076, 1, 0, 0, 0, 1078, 1079, 1, 0, 0, 0, 1079, 234, 1, 0, 0, 0, 1080, 1081, 3, 57, 21, 0, 1081, 1082, 1, 0, 0, 0, 1082, 1083, 6, 110, 10, 0, 1083, 236, 1, 0, 0, 0, 1084, 1085, 3, 59, 22, 0, 1085, 1086, 1, 0, 0, 0, 1086, 1087, 6, 111, 10, 0, 1087, 238, 1, 0, 0, 0, 1088, 1089, 3, 61, 23, 0, 1089, 1090, 1, 0, 0, 0, 1090, 1091, 6, 112, 10, 0, 1091, 240, 1, 0, 0, 0, 1092, 1093, 3, 63, 24, 0, 1093, 1094, 1, 0, 0, 0, 1094, 1095, 6, 113, 15, 0, 1095, 1096, 6, 113, 11, 0, 1096, 242, 1, 0, 0, 0, 1097, 1098, 3, 97, 41, 0, 1098, 1099, 1, 0, 0, 0, 1099, 1100, 6, 114, 19, 0, 1100, 244, 1, 0, 0, 0, 1101, 1102, 3, 101, 43, 0, 1102, 1103, 1, 0, 0, 0, 1103, 1104, 6, 115, 18, 0, 1104, 246, 1, 0, 0, 0, 1105, 1106, 3, 105, 45, 0, 1106, 1107, 1, 0, 0, 0, 1107, 1108, 6, 116, 22, 0, 1108, 248, 1, 0, 0, 0, 1109, 1110, 3, 129, 57, 0, 1110, 1111, 1, 0, 0, 0, 1111, 1112, 6, 117, 23, 0, 1112, 250, 1, 0, 0, 0, 1113, 1114, 3, 163, 74, 0, 1114, 1115, 1, 0, 0, 0, 1115, 1116, 6, 118, 24, 0, 1116, 252, 1, 0, 0, 0, 1117, 1118, 7, 12, 0, 0, 1118, 1119, 7, 2, 0, 0, 1119, 254, 1, 0, 0, 0, 1120, 1121, 3, 233, 109, 0, 1121, 1122, 1, 0, 0, 0, 1122, 1123, 6, 120, 25, 0, 1123, 256, 1, 0, 0, 0, 1124, 1125, 3, 57, 21, 0, 1125, 1126, 1, 0, 0, 0, 1126, 1127, 6, 121, 10, 0, 1127, 258, 1, 0, 0, 0, 1128, 1129, 3, 59, 22, 0, 1129, 1130, 1, 0, 0, 0, 1130, 1131, 6, 122, 10, 0, 1131, 260, 1, 0, 0, 0, 1132, 1133, 3, 61, 23, 0, 1133, 1134, 1, 0, 0, 0, 1134, 1135, 6, 123, 10, 0, 1135, 262, 1, 0, 0, 0, 1136, 1137, 3, 63, 24, 0, 1137, 1138, 1, 0, 0, 0, 1138, 1139, 6, 124, 15, 0, 1139, 1140, 6, 124, 11, 0, 1140, 264, 1, 0, 0, 0, 1141, 1142, 3, 165, 75, 0, 1142, 1143, 1, 0, 0, 0, 1143, 1144, 6, 125, 13, 0, 1144, 1145, 6, 125, 26, 0, 1145, 266, 1, 0, 0, 0, 1146, 1147, 7, 7, 0, 0, 1147, 1148, 7, 9, 0, 0, 1148, 1149, 1, 0, 0, 0, 1149, 1150, 6, 126, 27, 0, 1150, 268, 1, 0, 0, 0, 1151, 1152, 7, 19, 0, 0, 1152, 1153, 7, 1, 0, 0, 1153, 1154, 7, 5, 0, 0, 1154, 1155, 7, 10, 0, 0, 1155, 1156, 1, 0, 0, 0, 1156, 1157, 6, 127, 27, 0, 1157, 270, 1, 0, 0, 0, 1158, 1159, 8, 34, 0, 0, 1159, 272, 1, 0, 0, 0, 1160, 1162, 3, 271, 128, 0, 1161, 1160, 1, 0, 0, 0, 1162, 1163, 1, 0, 0, 0, 1163, 1161, 1, 0, 0, 0, 1163, 1164, 1, 0, 0, 0, 1164, 1165, 1, 0, 0, 0, 1165, 1166, 3, 337, 161, 0, 1166, 1168, 1, 0, 0, 0, 1167, 1161, 1, 0, 0, 0, 1167, 1168, 1, 0, 0, 0, 1168, 1170, 1, 0, 0, 0, 1169, 1171, 3, 271, 128, 0, 1170, 1169, 1, 0, 0, 0, 1171, 1172, 1, 0, 0, 0, 1172, 1170, 1, 0, 0, 0, 1172, 1173, 1, 0, 0, 0, 1173, 274, 1, 0, 0, 0, 1174, 1175, 3, 273, 129, 0, 1175, 1176, 1, 0, 0, 0, 1176, 1177, 6, 130, 28, 0, 1177, 276, 1, 0, 0, 0, 1178, 1179, 3, 57, 21, 0, 1179, 1180, 1, 0, 0, 0, 1180, 1181, 6, 131, 10, 0, 1181, 278, 1, 0, 0, 0, 1182, 1183, 3, 59, 22, 0, 1183, 1184, 1, 0, 0, 0, 1184, 1185, 6, 132, 10, 0, 1185, 280, 1, 0, 0, 0, 1186, 1187, 3, 61, 23, 0, 1187, 1188, 1, 0, 0, 0, 1188, 1189, 6, 133, 10, 0, 1189, 282, 1, 0, 0, 0, 1190, 1191, 3, 63, 24, 0, 1191, 1192, 1, 0, 0, 0, 1192, 1193, 6, 134, 15, 0, 1193, 1194, 6, 134, 11, 0, 1194, 1195, 6, 134, 11, 0, 1195, 284, 1, 0, 0, 0, 1196, 1197, 3, 97, 41, 0, 1197, 1198, 1, 0, 0, 0, 1198, 1199, 6, 135, 19, 0, 1199, 286, 1, 0, 0, 0, 1200, 1201, 3, 101, 43, 0, 1201, 1202, 1, 0, 0, 0, 1202, 1203, 6, 136, 18, 0, 1203, 288, 1, 0, 0, 0, 1204, 1205, 3, 105, 45, 0, 1205, 1206, 1, 0, 0, 0, 1206, 1207, 6, 137, 22, 0, 1207, 290, 1, 0, 0, 0, 1208, 1209, 3, 269, 127, 0, 1209, 1210, 1, 0, 0, 0, 1210, 1211, 6, 138, 29, 0, 1211, 292, 1, 0, 0, 0, 1212, 1213, 3, 233, 109, 0, 1213, 1214, 1, 0, 0, 0, 1214, 1215, 6, 139, 25, 0, 1215, 294, 1, 0, 0, 0, 1216, 1217, 3, 173, 79, 0, 1217, 1218, 1, 0, 0, 0, 1218, 1219, 6, 140, 30, 0, 1219, 296, 1, 0, 0, 0, 1220, 1221, 3, 129, 57, 0, 1221, 1222, 1, 0, 0, 0, 1222, 1223, 6, 141, 23, 0, 1223, 298, 1, 0, 0, 0, 1224, 1225, 3, 163, 74, 0, 1225, 1226, 1, 0, 0, 0, 1226, 1227, 6, 142, 24, 0, 1227, 300, 1, 0, 0, 0, 1228, 1229, 3, 57, 21, 0, 1229, 1230, 1, 0, 0, 0, 1230, 1231, 6, 143, 10, 0, 1231, 302, 1, 0, 0, 0, 1232, 1233, 3, 59, 22, 0, 1233, 1234, 1, 0, 0, 0, 1234, 1235, 6, 144, 10, 0, 1235, 304, 1, 0, 0, 0, 1236, 1237, 3, 61, 23, 0, 1237, 1238, 1, 0, 0, 0, 1238, 1239, 6, 145, 10, 0, 1239, 306, 1, 0, 0, 0, 1240, 1241, 3, 63, 24, 0, 1241, 1242, 1, 0, 0, 0, 1242, 1243, 6, 146, 15, 0, 1243, 1244, 6, 146, 11, 0, 1244, 308, 1, 0, 0, 0, 1245, 1246, 3, 105, 45, 0, 1246, 1247, 1, 0, 0, 0, 1247, 1248, 6, 147, 22, 0, 1248, 310, 1, 0, 0, 0, 1249, 1250, 3, 129, 57, 0, 1250, 1251, 1, 0, 0, 0, 1251, 1252, 6, 148, 23, 0, 1252, 312, 1, 0, 0, 0, 1253, 1254, 3, 163, 74, 0, 1254, 1255, 1, 0, 0, 0, 1255, 1256, 6, 149, 24, 0, 1256, 314, 1, 0, 0, 0, 1257, 1258, 3, 173, 79, 0, 1258, 1259, 1, 0, 0, 0, 1259, 1260, 6, 150, 30, 0, 1260, 316, 1, 0, 0, 0, 1261, 1262, 3, 169, 77, 0, 1262, 1263, 1, 0, 0, 0, 1263, 1264, 6, 151, 31, 0, 1264, 318, 1, 0, 0, 0, 1265, 1266, 3, 57, 21, 0, 1266, 1267, 1, 0, 0, 0, 1267, 1268, 6, 152, 10, 0, 1268, 320, 1, 0, 0, 0, 1269, 1270, 3, 59, 22, 0, 1270, 1271, 1, 0, 0, 0, 1271, 1272, 6, 153, 10, 0, 1272, 322, 1, 0, 0, 0, 1273, 1274, 3, 61, 23, 0, 1274, 1275, 1, 0, 0, 0, 1275, 1276, 6, 154, 10, 0, 1276, 324, 1, 0, 0, 0, 1277, 1278, 3, 63, 24, 0, 1278, 1279, 1, 0, 0, 0, 1279, 1280, 6, 155, 15, 0, 1280, 1281, 6, 155, 11, 0, 1281, 326, 1, 0, 0, 0, 1282, 1283, 7, 1, 0, 0, 1283, 1284, 7, 9, 0, 0, 1284, 1285, 7, 15, 0, 0, 1285, 1286, 7, 7, 0, 0, 1286, 328, 1, 0, 0, 0, 1287, 1288, 3, 57, 21, 0, 1288, 1289, 1, 0, 0, 0, 1289, 1290, 6, 157, 10, 0, 1290, 330, 1, 0, 0, 0, 1291, 1292, 3, 59, 22, 0, 1292, 1293, 1, 0, 0, 0, 1293, 1294, 6, 158, 10, 0, 1294, 332, 1, 0, 0, 0, 1295, 1296, 3, 61, 23, 0, 1296, 1297, 1, 0, 0, 0, 1297, 1298, 6, 159, 10, 0, 1298, 334, 1, 0, 0, 0, 1299, 1300, 3, 167, 76, 0, 1300, 1301, 1, 0, 0, 0, 1301, 1302, 6, 160, 16, 0, 1302, 1303, 6, 160, 11, 0, 1303, 336, 1, 0, 0, 0, 1304, 1305, 5, 58, 0, 0, 1305, 338, 1, 0, 0, 0, 1306, 1312, 3, 75, 30, 0, 1307, 1312, 3, 65, 25, 0, 1308, 1312, 3, 105, 45, 0, 1309, 1312, 3, 67, 26, 0, 1310, 1312, 3, 81, 33, 0, 1311, 1306, 1, 0, 0, 0, 1311, 1307, 1, 0, 0, 0, 1311, 1308, 1, 0, 0, 0, 1311, 1309, 1, 0, 0, 0, 1311, 1310, 1, 0, 0, 0, 1312, 1313, 1, 0, 0, 0, 1313, 1311, 1, 0, 0, 0, 1313, 1314, 1, 0, 0, 0, 1314, 340, 1, 0, 0, 0, 1315, 1316, 3, 57, 21, 0, 1316, 1317, 1, 0, 0, 0, 1317, 1318, 6, 163, 10, 0, 1318, 342, 1, 0, 0, 0, 1319, 1320, 3, 59, 22, 0, 1320, 1321, 1, 0, 0, 0, 1321, 1322, 6, 164, 10, 0, 1322, 344, 1, 0, 0, 0, 1323, 1324, 3, 61, 23, 0, 1324, 1325, 1, 0, 0, 0, 1325, 1326, 6, 165, 10, 0, 1326, 346, 1, 0, 0, 0, 1327, 1328, 3, 63, 24, 0, 1328, 1329, 1, 0, 0, 0, 1329, 1330, 6, 166, 15, 0, 1330, 1331, 6, 166, 11, 0, 1331, 348, 1, 0, 0, 0, 1332, 1333, 3, 337, 161, 0, 1333, 1334, 1, 0, 0, 0, 1334, 1335, 6, 167, 17, 0, 1335, 350, 1, 0, 0, 0, 1336, 1337, 3, 101, 43, 0, 1337, 1338, 1, 0, 0, 0, 1338, 1339, 6, 168, 18, 0, 1339, 352, 1, 0, 0, 0, 1340, 1341, 3, 105, 45, 0, 1341, 1342, 1, 0, 0, 0, 1342, 1343, 6, 169, 22, 0, 1343, 354, 1, 0, 0, 0, 1344, 1345, 3, 267, 126, 0, 1345, 1346, 1, 0, 0, 0, 1346, 1347, 6, 170, 32, 0, 1347, 1348, 6, 170, 33, 0, 1348, 356, 1, 0, 0, 0, 1349, 1350, 3, 207, 96, 0, 1350, 1351, 1, 0, 0, 0, 1351, 1352, 6, 171, 20, 0, 1352, 358, 1, 0, 0, 0, 1353, 1354, 3, 85, 35, 0, 1354, 1355, 1, 0, 0, 0, 1355, 1356, 6, 172, 21, 0, 1356, 360, 1, 0, 0, 0, 1357, 1358, 3, 57, 21, 0, 1358, 1359, 1, 0, 0, 0, 1359, 1360, 6, 173, 10, 0, 1360, 362, 1, 0, 0, 0, 1361, 1362, 3, 59, 22, 0, 1362, 1363, 1, 0, 0, 0, 1363, 1364, 6, 174, 10, 0, 1364, 364, 1, 0, 0, 0, 1365, 1366, 3, 61, 23, 0, 1366, 1367, 1, 0, 0, 0, 1367, 1368, 6, 175, 10, 0, 1368, 366, 1, 0, 0, 0, 1369, 1370, 3, 63, 24, 0, 1370, 1371, 1, 0, 0, 0, 1371, 1372, 6, 176, 15, 0, 1372, 1373, 6, 176, 11, 0, 1373, 1374, 6, 176, 11, 0, 1374, 368, 1, 0, 0, 0, 1375, 1376, 3, 101, 43, 0, 1376, 1377, 1, 0, 0, 0, 1377, 1378, 6, 177, 18, 0, 1378, 370, 1, 0, 0, 0, 1379, 1380, 3, 105, 45, 0, 1380, 1381, 1, 0, 0, 0, 1381, 1382, 6, 178, 22, 0, 1382, 372, 1, 0, 0, 0, 1383, 1384, 3, 233, 109, 0, 1384, 1385, 1, 0, 0, 0, 1385, 1386, 6, 179, 25, 0, 1386, 374, 1, 0, 0, 0, 1387, 1388, 3, 57, 21, 0, 1388, 1389, 1, 0, 0, 0, 1389, 1390, 6, 180, 10, 0, 1390, 376, 1, 0, 0, 0, 1391, 1392, 3, 59, 22, 0, 1392, 1393, 1, 0, 0, 0, 1393, 1394, 6, 181, 10, 0, 1394, 378, 1, 0, 0, 0, 1395, 1396, 3, 61, 23, 0, 1396, 1397, 1, 0, 0, 0, 1397, 1398, 6, 182, 10, 0, 1398, 380, 1, 0, 0, 0, 1399, 1400, 3, 63, 24, 0, 1400, 1401, 1, 0, 0, 0, 1401, 1402, 6, 183, 15, 0, 1402, 1403, 6, 183, 11, 0, 1403, 382, 1, 0, 0, 0, 1404, 1405, 3, 207, 96, 0, 1405, 1406, 1, 0, 0, 0, 1406, 1407, 6, 184, 20, 0, 1407, 1408, 6, 184, 11, 0, 1408, 1409, 6, 184, 34, 0, 1409, 384, 1, 0, 0, 0, 1410, 1411, 3, 85, 35, 0, 1411, 1412, 1, 0, 0, 0, 1412, 1413, 6, 185, 21, 0, 1413, 1414, 6, 185, 11, 0, 1414, 1415, 6, 185, 34, 0, 1415, 386, 1, 0, 0, 0, 1416, 1417, 3, 57, 21, 0, 1417, 1418, 1, 0, 0, 0, 1418, 1419, 6, 186, 10, 0, 1419, 388, 1, 0, 0, 0, 1420, 1421, 3, 59, 22, 0, 1421, 1422, 1, 0, 0, 0, 1422, 1423, 6, 187, 10, 0, 1423, 390, 1, 0, 0, 0, 1424, 1425, 3, 61, 23, 0, 1425, 1426, 1, 0, 0, 0, 1426, 1427, 6, 188, 10, 0, 1427, 392, 1, 0, 0, 0, 1428, 1429, 3, 337, 161, 0, 1429, 1430, 1, 0, 0, 0, 1430, 1431, 6, 189, 17, 0, 1431, 1432, 6, 189, 11, 0, 1432, 1433, 6, 189, 9, 0, 1433, 394, 1, 0, 0, 0, 1434, 1435, 3, 101, 43, 0, 1435, 1436, 1, 0, 0, 0, 1436, 1437, 6, 190, 18, 0, 1437, 1438, 6, 190, 11, 0, 1438, 1439, 6, 190, 9, 0, 1439, 396, 1, 0, 0, 0, 1440, 1441, 3, 57, 21, 0, 1441, 1442, 1, 0, 0, 0, 1442, 1443, 6, 191, 10, 0, 1443, 398, 1, 0, 0, 0, 1444, 1445, 3, 59, 22, 0, 1445, 1446, 1, 0, 0, 0, 1446, 1447, 6, 192, 10, 0, 1447, 400, 1, 0, 0, 0, 1448, 1449, 3, 61, 23, 0, 1449, 1450, 1, 0, 0, 0, 1450, 1451, 6, 193, 10, 0, 1451, 402, 1, 0, 0, 0, 1452, 1453, 3, 173, 79, 0, 1453, 1454, 1, 0, 0, 0, 1454, 1455, 6, 194, 11, 0, 1455, 1456, 6, 194, 0, 0, 1456, 1457, 6, 194, 30, 0, 1457, 404, 1, 0, 0, 0, 1458, 1459, 3, 169, 77, 0, 1459, 1460, 1, 0, 0, 0, 1460, 1461, 6, 195, 11, 0, 1461, 1462, 6, 195, 0, 0, 1462, 1463, 6, 195, 31, 0, 1463, 406, 1, 0, 0, 0, 1464, 1465, 3, 91, 38, 0, 1465, 1466, 1, 0, 0, 0, 1466, 1467, 6, 196, 11, 0, 1467, 1468, 6, 196, 0, 0, 1468, 1469, 6, 196, 35, 0, 1469, 408, 1, 0, 0, 0, 1470, 1471, 3, 63, 24, 0, 1471, 1472, 1, 0, 0, 0, 1472, 1473, 6, 197, 15, 0, 1473, 1474, 6, 197, 11, 0, 1474, 410, 1, 0, 0, 0, 65, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 588, 598, 602, 605, 614, 616, 627, 646, 651, 660, 667, 672, 674, 685, 693, 696, 698, 703, 708, 714, 721, 726, 732, 735, 743, 747, 874, 879, 886, 888, 904, 909, 914, 916, 922, 999, 1004, 1051, 1055, 1060, 1065, 1070, 1072, 1076, 1078, 1163, 1167, 1172, 1311, 1313, 36, 5, 1, 0, 5, 4, 0, 5, 6, 0, 5, 2, 0, 5, 3, 0, 5, 8, 0, 5, 5, 0, 5, 9, 0, 5, 11, 0, 5, 13, 0, 0, 1, 0, 4, 0, 0, 7, 19, 0, 7, 65, 0, 5, 0, 0, 7, 25, 0, 7, 66, 0, 7, 104, 0, 7, 34, 0, 7, 32, 0, 7, 76, 0, 7, 26, 0, 7, 36, 0, 7, 48, 0, 7, 64, 0, 7, 80, 0, 5, 10, 0, 5, 7, 0, 7, 90, 0, 7, 89, 0, 7, 68, 0, 7, 67, 0, 7, 88, 0, 5, 12, 0, 5, 14, 0, 7, 29, 0] \ No newline at end of file +[4, 0, 120, 1479, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 2, 85, 7, 85, 2, 86, 7, 86, 2, 87, 7, 87, 2, 88, 7, 88, 2, 89, 7, 89, 2, 90, 7, 90, 2, 91, 7, 91, 2, 92, 7, 92, 2, 93, 7, 93, 2, 94, 7, 94, 2, 95, 7, 95, 2, 96, 7, 96, 2, 97, 7, 97, 2, 98, 7, 98, 2, 99, 7, 99, 2, 100, 7, 100, 2, 101, 7, 101, 2, 102, 7, 102, 2, 103, 7, 103, 2, 104, 7, 104, 2, 105, 7, 105, 2, 106, 7, 106, 2, 107, 7, 107, 2, 108, 7, 108, 2, 109, 7, 109, 2, 110, 7, 110, 2, 111, 7, 111, 2, 112, 7, 112, 2, 113, 7, 113, 2, 114, 7, 114, 2, 115, 7, 115, 2, 116, 7, 116, 2, 117, 7, 117, 2, 118, 7, 118, 2, 119, 7, 119, 2, 120, 7, 120, 2, 121, 7, 121, 2, 122, 7, 122, 2, 123, 7, 123, 2, 124, 7, 124, 2, 125, 7, 125, 2, 126, 7, 126, 2, 127, 7, 127, 2, 128, 7, 128, 2, 129, 7, 129, 2, 130, 7, 130, 2, 131, 7, 131, 2, 132, 7, 132, 2, 133, 7, 133, 2, 134, 7, 134, 2, 135, 7, 135, 2, 136, 7, 136, 2, 137, 7, 137, 2, 138, 7, 138, 2, 139, 7, 139, 2, 140, 7, 140, 2, 141, 7, 141, 2, 142, 7, 142, 2, 143, 7, 143, 2, 144, 7, 144, 2, 145, 7, 145, 2, 146, 7, 146, 2, 147, 7, 147, 2, 148, 7, 148, 2, 149, 7, 149, 2, 150, 7, 150, 2, 151, 7, 151, 2, 152, 7, 152, 2, 153, 7, 153, 2, 154, 7, 154, 2, 155, 7, 155, 2, 156, 7, 156, 2, 157, 7, 157, 2, 158, 7, 158, 2, 159, 7, 159, 2, 160, 7, 160, 2, 161, 7, 161, 2, 162, 7, 162, 2, 163, 7, 163, 2, 164, 7, 164, 2, 165, 7, 165, 2, 166, 7, 166, 2, 167, 7, 167, 2, 168, 7, 168, 2, 169, 7, 169, 2, 170, 7, 170, 2, 171, 7, 171, 2, 172, 7, 172, 2, 173, 7, 173, 2, 174, 7, 174, 2, 175, 7, 175, 2, 176, 7, 176, 2, 177, 7, 177, 2, 178, 7, 178, 2, 179, 7, 179, 2, 180, 7, 180, 2, 181, 7, 181, 2, 182, 7, 182, 2, 183, 7, 183, 2, 184, 7, 184, 2, 185, 7, 185, 2, 186, 7, 186, 2, 187, 7, 187, 2, 188, 7, 188, 2, 189, 7, 189, 2, 190, 7, 190, 2, 191, 7, 191, 2, 192, 7, 192, 2, 193, 7, 193, 2, 194, 7, 194, 2, 195, 7, 195, 2, 196, 7, 196, 2, 197, 7, 197, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 4, 19, 578, 8, 19, 11, 19, 12, 19, 579, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 5, 20, 588, 8, 20, 10, 20, 12, 20, 591, 9, 20, 1, 20, 3, 20, 594, 8, 20, 1, 20, 3, 20, 597, 8, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 5, 21, 606, 8, 21, 10, 21, 12, 21, 609, 9, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 22, 4, 22, 617, 8, 22, 11, 22, 12, 22, 618, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 28, 1, 28, 3, 28, 638, 8, 28, 1, 28, 4, 28, 641, 8, 28, 11, 28, 12, 28, 642, 1, 29, 1, 29, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 3, 31, 652, 8, 31, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 3, 33, 659, 8, 33, 1, 34, 1, 34, 1, 34, 5, 34, 664, 8, 34, 10, 34, 12, 34, 667, 9, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 675, 8, 34, 10, 34, 12, 34, 678, 9, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 3, 34, 685, 8, 34, 1, 34, 3, 34, 688, 8, 34, 3, 34, 690, 8, 34, 1, 35, 4, 35, 693, 8, 35, 11, 35, 12, 35, 694, 1, 36, 4, 36, 698, 8, 36, 11, 36, 12, 36, 699, 1, 36, 1, 36, 5, 36, 704, 8, 36, 10, 36, 12, 36, 707, 9, 36, 1, 36, 1, 36, 4, 36, 711, 8, 36, 11, 36, 12, 36, 712, 1, 36, 4, 36, 716, 8, 36, 11, 36, 12, 36, 717, 1, 36, 1, 36, 5, 36, 722, 8, 36, 10, 36, 12, 36, 725, 9, 36, 3, 36, 727, 8, 36, 1, 36, 1, 36, 1, 36, 1, 36, 4, 36, 733, 8, 36, 11, 36, 12, 36, 734, 1, 36, 1, 36, 3, 36, 739, 8, 36, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 62, 1, 62, 1, 62, 1, 63, 1, 63, 1, 64, 1, 64, 1, 64, 1, 65, 1, 65, 1, 66, 1, 66, 1, 66, 1, 67, 1, 67, 1, 68, 1, 68, 1, 69, 1, 69, 1, 70, 1, 70, 1, 71, 1, 71, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 73, 1, 73, 1, 73, 1, 73, 1, 74, 1, 74, 1, 74, 3, 74, 871, 8, 74, 1, 74, 5, 74, 874, 8, 74, 10, 74, 12, 74, 877, 9, 74, 1, 74, 1, 74, 4, 74, 881, 8, 74, 11, 74, 12, 74, 882, 3, 74, 885, 8, 74, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 77, 1, 77, 5, 77, 899, 8, 77, 10, 77, 12, 77, 902, 9, 77, 1, 77, 1, 77, 3, 77, 906, 8, 77, 1, 77, 4, 77, 909, 8, 77, 11, 77, 12, 77, 910, 3, 77, 913, 8, 77, 1, 78, 1, 78, 4, 78, 917, 8, 78, 11, 78, 12, 78, 918, 1, 78, 1, 78, 1, 79, 1, 79, 1, 80, 1, 80, 1, 80, 1, 80, 1, 81, 1, 81, 1, 81, 1, 81, 1, 82, 1, 82, 1, 82, 1, 82, 1, 83, 1, 83, 1, 83, 1, 83, 1, 83, 1, 84, 1, 84, 1, 84, 1, 84, 1, 84, 1, 85, 1, 85, 1, 85, 1, 85, 1, 86, 1, 86, 1, 86, 1, 86, 1, 87, 1, 87, 1, 87, 1, 87, 1, 88, 1, 88, 1, 88, 1, 88, 1, 88, 1, 89, 1, 89, 1, 89, 1, 89, 1, 90, 1, 90, 1, 90, 1, 90, 1, 91, 1, 91, 1, 91, 1, 91, 1, 92, 1, 92, 1, 92, 1, 92, 1, 93, 1, 93, 1, 93, 1, 93, 1, 94, 1, 94, 1, 94, 1, 94, 1, 94, 1, 94, 1, 94, 1, 94, 1, 94, 1, 95, 1, 95, 1, 95, 3, 95, 996, 8, 95, 1, 96, 4, 96, 999, 8, 96, 11, 96, 12, 96, 1000, 1, 97, 1, 97, 1, 97, 1, 97, 1, 98, 1, 98, 1, 98, 1, 98, 1, 99, 1, 99, 1, 99, 1, 99, 1, 100, 1, 100, 1, 100, 1, 100, 1, 101, 1, 101, 1, 101, 1, 101, 1, 102, 1, 102, 1, 102, 1, 102, 1, 102, 1, 103, 1, 103, 1, 103, 1, 103, 1, 104, 1, 104, 1, 104, 1, 104, 1, 105, 1, 105, 1, 105, 1, 105, 1, 105, 1, 106, 1, 106, 1, 106, 1, 106, 1, 106, 1, 107, 1, 107, 1, 107, 1, 107, 3, 107, 1050, 8, 107, 1, 108, 1, 108, 3, 108, 1054, 8, 108, 1, 108, 5, 108, 1057, 8, 108, 10, 108, 12, 108, 1060, 9, 108, 1, 108, 1, 108, 3, 108, 1064, 8, 108, 1, 108, 4, 108, 1067, 8, 108, 11, 108, 12, 108, 1068, 3, 108, 1071, 8, 108, 1, 109, 1, 109, 4, 109, 1075, 8, 109, 11, 109, 12, 109, 1076, 1, 110, 1, 110, 1, 110, 1, 110, 1, 111, 1, 111, 1, 111, 1, 111, 1, 112, 1, 112, 1, 112, 1, 112, 1, 113, 1, 113, 1, 113, 1, 113, 1, 113, 1, 114, 1, 114, 1, 114, 1, 114, 1, 115, 1, 115, 1, 115, 1, 115, 1, 116, 1, 116, 1, 116, 1, 116, 1, 117, 1, 117, 1, 117, 1, 117, 1, 117, 1, 118, 1, 118, 1, 118, 1, 118, 1, 118, 1, 119, 1, 119, 1, 119, 1, 120, 1, 120, 1, 120, 1, 120, 1, 121, 1, 121, 1, 121, 1, 121, 1, 122, 1, 122, 1, 122, 1, 122, 1, 123, 1, 123, 1, 123, 1, 123, 1, 124, 1, 124, 1, 124, 1, 124, 1, 124, 1, 125, 1, 125, 1, 125, 1, 125, 1, 125, 1, 126, 1, 126, 1, 126, 1, 126, 1, 126, 1, 127, 1, 127, 1, 127, 1, 127, 1, 127, 1, 127, 1, 127, 1, 128, 1, 128, 1, 129, 4, 129, 1162, 8, 129, 11, 129, 12, 129, 1163, 1, 129, 1, 129, 3, 129, 1168, 8, 129, 1, 129, 4, 129, 1171, 8, 129, 11, 129, 12, 129, 1172, 1, 130, 1, 130, 1, 130, 1, 130, 1, 131, 1, 131, 1, 131, 1, 131, 1, 132, 1, 132, 1, 132, 1, 132, 1, 133, 1, 133, 1, 133, 1, 133, 1, 134, 1, 134, 1, 134, 1, 134, 1, 134, 1, 134, 1, 135, 1, 135, 1, 135, 1, 135, 1, 136, 1, 136, 1, 136, 1, 136, 1, 137, 1, 137, 1, 137, 1, 137, 1, 138, 1, 138, 1, 138, 1, 138, 1, 139, 1, 139, 1, 139, 1, 139, 1, 140, 1, 140, 1, 140, 1, 140, 1, 141, 1, 141, 1, 141, 1, 141, 1, 141, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 143, 1, 143, 1, 143, 1, 143, 1, 144, 1, 144, 1, 144, 1, 144, 1, 145, 1, 145, 1, 145, 1, 145, 1, 146, 1, 146, 1, 146, 1, 146, 1, 146, 1, 147, 1, 147, 1, 147, 1, 147, 1, 148, 1, 148, 1, 148, 1, 148, 1, 148, 1, 149, 1, 149, 1, 149, 1, 149, 1, 149, 1, 150, 1, 150, 1, 150, 1, 150, 1, 151, 1, 151, 1, 151, 1, 151, 1, 152, 1, 152, 1, 152, 1, 152, 1, 153, 1, 153, 1, 153, 1, 153, 1, 154, 1, 154, 1, 154, 1, 154, 1, 155, 1, 155, 1, 155, 1, 155, 1, 155, 1, 156, 1, 156, 1, 156, 1, 156, 1, 156, 1, 157, 1, 157, 1, 157, 1, 157, 1, 158, 1, 158, 1, 158, 1, 158, 1, 159, 1, 159, 1, 159, 1, 159, 1, 160, 1, 160, 1, 160, 1, 160, 1, 160, 1, 161, 1, 161, 1, 162, 1, 162, 1, 162, 1, 162, 1, 162, 4, 162, 1316, 8, 162, 11, 162, 12, 162, 1317, 1, 163, 1, 163, 1, 163, 1, 163, 1, 164, 1, 164, 1, 164, 1, 164, 1, 165, 1, 165, 1, 165, 1, 165, 1, 166, 1, 166, 1, 166, 1, 166, 1, 166, 1, 167, 1, 167, 1, 167, 1, 167, 1, 168, 1, 168, 1, 168, 1, 168, 1, 169, 1, 169, 1, 169, 1, 169, 1, 170, 1, 170, 1, 170, 1, 170, 1, 170, 1, 171, 1, 171, 1, 171, 1, 171, 1, 172, 1, 172, 1, 172, 1, 172, 1, 173, 1, 173, 1, 173, 1, 173, 1, 174, 1, 174, 1, 174, 1, 174, 1, 175, 1, 175, 1, 175, 1, 175, 1, 176, 1, 176, 1, 176, 1, 176, 1, 176, 1, 176, 1, 177, 1, 177, 1, 177, 1, 177, 1, 178, 1, 178, 1, 178, 1, 178, 1, 179, 1, 179, 1, 179, 1, 179, 1, 180, 1, 180, 1, 180, 1, 180, 1, 181, 1, 181, 1, 181, 1, 181, 1, 182, 1, 182, 1, 182, 1, 182, 1, 183, 1, 183, 1, 183, 1, 183, 1, 183, 1, 184, 1, 184, 1, 184, 1, 184, 1, 184, 1, 184, 1, 185, 1, 185, 1, 185, 1, 185, 1, 185, 1, 185, 1, 186, 1, 186, 1, 186, 1, 186, 1, 187, 1, 187, 1, 187, 1, 187, 1, 188, 1, 188, 1, 188, 1, 188, 1, 189, 1, 189, 1, 189, 1, 189, 1, 189, 1, 189, 1, 190, 1, 190, 1, 190, 1, 190, 1, 190, 1, 190, 1, 191, 1, 191, 1, 191, 1, 191, 1, 192, 1, 192, 1, 192, 1, 192, 1, 193, 1, 193, 1, 193, 1, 193, 1, 194, 1, 194, 1, 194, 1, 194, 1, 194, 1, 194, 1, 195, 1, 195, 1, 195, 1, 195, 1, 195, 1, 195, 1, 196, 1, 196, 1, 196, 1, 196, 1, 196, 1, 196, 1, 197, 1, 197, 1, 197, 1, 197, 1, 197, 2, 607, 676, 0, 198, 15, 1, 17, 2, 19, 3, 21, 4, 23, 5, 25, 6, 27, 7, 29, 8, 31, 9, 33, 10, 35, 11, 37, 12, 39, 13, 41, 14, 43, 15, 45, 16, 47, 17, 49, 18, 51, 19, 53, 20, 55, 21, 57, 22, 59, 23, 61, 24, 63, 0, 65, 0, 67, 0, 69, 0, 71, 0, 73, 0, 75, 0, 77, 0, 79, 0, 81, 0, 83, 25, 85, 26, 87, 27, 89, 28, 91, 29, 93, 30, 95, 31, 97, 32, 99, 33, 101, 34, 103, 35, 105, 36, 107, 37, 109, 38, 111, 39, 113, 40, 115, 41, 117, 42, 119, 43, 121, 44, 123, 45, 125, 46, 127, 47, 129, 48, 131, 49, 133, 50, 135, 51, 137, 52, 139, 53, 141, 54, 143, 55, 145, 56, 147, 57, 149, 58, 151, 59, 153, 60, 155, 61, 157, 62, 159, 63, 161, 0, 163, 64, 165, 65, 167, 66, 169, 67, 171, 0, 173, 68, 175, 69, 177, 70, 179, 71, 181, 0, 183, 0, 185, 72, 187, 73, 189, 74, 191, 0, 193, 0, 195, 0, 197, 0, 199, 0, 201, 0, 203, 75, 205, 0, 207, 76, 209, 0, 211, 0, 213, 77, 215, 78, 217, 79, 219, 0, 221, 0, 223, 0, 225, 0, 227, 0, 229, 0, 231, 0, 233, 80, 235, 81, 237, 82, 239, 83, 241, 0, 243, 0, 245, 0, 247, 0, 249, 0, 251, 0, 253, 84, 255, 0, 257, 85, 259, 86, 261, 87, 263, 0, 265, 0, 267, 88, 269, 89, 271, 0, 273, 90, 275, 0, 277, 91, 279, 92, 281, 93, 283, 0, 285, 0, 287, 0, 289, 0, 291, 0, 293, 0, 295, 0, 297, 0, 299, 0, 301, 94, 303, 95, 305, 96, 307, 0, 309, 0, 311, 0, 313, 0, 315, 0, 317, 0, 319, 97, 321, 98, 323, 99, 325, 0, 327, 100, 329, 101, 331, 102, 333, 103, 335, 0, 337, 104, 339, 105, 341, 106, 343, 107, 345, 108, 347, 0, 349, 0, 351, 0, 353, 0, 355, 0, 357, 0, 359, 0, 361, 109, 363, 110, 365, 111, 367, 0, 369, 0, 371, 0, 373, 0, 375, 112, 377, 113, 379, 114, 381, 0, 383, 0, 385, 0, 387, 115, 389, 116, 391, 117, 393, 0, 395, 0, 397, 118, 399, 119, 401, 120, 403, 0, 405, 0, 407, 0, 409, 0, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 35, 2, 0, 68, 68, 100, 100, 2, 0, 73, 73, 105, 105, 2, 0, 83, 83, 115, 115, 2, 0, 69, 69, 101, 101, 2, 0, 67, 67, 99, 99, 2, 0, 84, 84, 116, 116, 2, 0, 82, 82, 114, 114, 2, 0, 79, 79, 111, 111, 2, 0, 80, 80, 112, 112, 2, 0, 78, 78, 110, 110, 2, 0, 72, 72, 104, 104, 2, 0, 86, 86, 118, 118, 2, 0, 65, 65, 97, 97, 2, 0, 76, 76, 108, 108, 2, 0, 88, 88, 120, 120, 2, 0, 70, 70, 102, 102, 2, 0, 77, 77, 109, 109, 2, 0, 71, 71, 103, 103, 2, 0, 75, 75, 107, 107, 2, 0, 87, 87, 119, 119, 2, 0, 85, 85, 117, 117, 6, 0, 9, 10, 13, 13, 32, 32, 47, 47, 91, 91, 93, 93, 2, 0, 10, 10, 13, 13, 3, 0, 9, 10, 13, 13, 32, 32, 1, 0, 48, 57, 2, 0, 65, 90, 97, 122, 8, 0, 34, 34, 78, 78, 82, 82, 84, 84, 92, 92, 110, 110, 114, 114, 116, 116, 4, 0, 10, 10, 13, 13, 34, 34, 92, 92, 2, 0, 43, 43, 45, 45, 1, 0, 96, 96, 2, 0, 66, 66, 98, 98, 2, 0, 89, 89, 121, 121, 11, 0, 9, 10, 13, 13, 32, 32, 34, 34, 44, 44, 47, 47, 58, 58, 61, 61, 91, 91, 93, 93, 124, 124, 2, 0, 42, 42, 47, 47, 11, 0, 9, 10, 13, 13, 32, 32, 34, 35, 44, 44, 47, 47, 58, 58, 60, 60, 62, 63, 92, 92, 124, 124, 1507, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 1, 61, 1, 0, 0, 0, 1, 83, 1, 0, 0, 0, 1, 85, 1, 0, 0, 0, 1, 87, 1, 0, 0, 0, 1, 89, 1, 0, 0, 0, 1, 91, 1, 0, 0, 0, 1, 93, 1, 0, 0, 0, 1, 95, 1, 0, 0, 0, 1, 97, 1, 0, 0, 0, 1, 99, 1, 0, 0, 0, 1, 101, 1, 0, 0, 0, 1, 103, 1, 0, 0, 0, 1, 105, 1, 0, 0, 0, 1, 107, 1, 0, 0, 0, 1, 109, 1, 0, 0, 0, 1, 111, 1, 0, 0, 0, 1, 113, 1, 0, 0, 0, 1, 115, 1, 0, 0, 0, 1, 117, 1, 0, 0, 0, 1, 119, 1, 0, 0, 0, 1, 121, 1, 0, 0, 0, 1, 123, 1, 0, 0, 0, 1, 125, 1, 0, 0, 0, 1, 127, 1, 0, 0, 0, 1, 129, 1, 0, 0, 0, 1, 131, 1, 0, 0, 0, 1, 133, 1, 0, 0, 0, 1, 135, 1, 0, 0, 0, 1, 137, 1, 0, 0, 0, 1, 139, 1, 0, 0, 0, 1, 141, 1, 0, 0, 0, 1, 143, 1, 0, 0, 0, 1, 145, 1, 0, 0, 0, 1, 147, 1, 0, 0, 0, 1, 149, 1, 0, 0, 0, 1, 151, 1, 0, 0, 0, 1, 153, 1, 0, 0, 0, 1, 155, 1, 0, 0, 0, 1, 157, 1, 0, 0, 0, 1, 159, 1, 0, 0, 0, 1, 161, 1, 0, 0, 0, 1, 163, 1, 0, 0, 0, 1, 165, 1, 0, 0, 0, 1, 167, 1, 0, 0, 0, 1, 169, 1, 0, 0, 0, 1, 173, 1, 0, 0, 0, 1, 175, 1, 0, 0, 0, 1, 177, 1, 0, 0, 0, 1, 179, 1, 0, 0, 0, 2, 181, 1, 0, 0, 0, 2, 183, 1, 0, 0, 0, 2, 185, 1, 0, 0, 0, 2, 187, 1, 0, 0, 0, 2, 189, 1, 0, 0, 0, 3, 191, 1, 0, 0, 0, 3, 193, 1, 0, 0, 0, 3, 195, 1, 0, 0, 0, 3, 197, 1, 0, 0, 0, 3, 199, 1, 0, 0, 0, 3, 201, 1, 0, 0, 0, 3, 203, 1, 0, 0, 0, 3, 207, 1, 0, 0, 0, 3, 209, 1, 0, 0, 0, 3, 211, 1, 0, 0, 0, 3, 213, 1, 0, 0, 0, 3, 215, 1, 0, 0, 0, 3, 217, 1, 0, 0, 0, 4, 219, 1, 0, 0, 0, 4, 221, 1, 0, 0, 0, 4, 223, 1, 0, 0, 0, 4, 225, 1, 0, 0, 0, 4, 227, 1, 0, 0, 0, 4, 233, 1, 0, 0, 0, 4, 235, 1, 0, 0, 0, 4, 237, 1, 0, 0, 0, 4, 239, 1, 0, 0, 0, 5, 241, 1, 0, 0, 0, 5, 243, 1, 0, 0, 0, 5, 245, 1, 0, 0, 0, 5, 247, 1, 0, 0, 0, 5, 249, 1, 0, 0, 0, 5, 251, 1, 0, 0, 0, 5, 253, 1, 0, 0, 0, 5, 255, 1, 0, 0, 0, 5, 257, 1, 0, 0, 0, 5, 259, 1, 0, 0, 0, 5, 261, 1, 0, 0, 0, 6, 263, 1, 0, 0, 0, 6, 265, 1, 0, 0, 0, 6, 267, 1, 0, 0, 0, 6, 269, 1, 0, 0, 0, 6, 273, 1, 0, 0, 0, 6, 275, 1, 0, 0, 0, 6, 277, 1, 0, 0, 0, 6, 279, 1, 0, 0, 0, 6, 281, 1, 0, 0, 0, 7, 283, 1, 0, 0, 0, 7, 285, 1, 0, 0, 0, 7, 287, 1, 0, 0, 0, 7, 289, 1, 0, 0, 0, 7, 291, 1, 0, 0, 0, 7, 293, 1, 0, 0, 0, 7, 295, 1, 0, 0, 0, 7, 297, 1, 0, 0, 0, 7, 299, 1, 0, 0, 0, 7, 301, 1, 0, 0, 0, 7, 303, 1, 0, 0, 0, 7, 305, 1, 0, 0, 0, 8, 307, 1, 0, 0, 0, 8, 309, 1, 0, 0, 0, 8, 311, 1, 0, 0, 0, 8, 313, 1, 0, 0, 0, 8, 315, 1, 0, 0, 0, 8, 317, 1, 0, 0, 0, 8, 319, 1, 0, 0, 0, 8, 321, 1, 0, 0, 0, 8, 323, 1, 0, 0, 0, 9, 325, 1, 0, 0, 0, 9, 327, 1, 0, 0, 0, 9, 329, 1, 0, 0, 0, 9, 331, 1, 0, 0, 0, 9, 333, 1, 0, 0, 0, 10, 335, 1, 0, 0, 0, 10, 337, 1, 0, 0, 0, 10, 339, 1, 0, 0, 0, 10, 341, 1, 0, 0, 0, 10, 343, 1, 0, 0, 0, 10, 345, 1, 0, 0, 0, 11, 347, 1, 0, 0, 0, 11, 349, 1, 0, 0, 0, 11, 351, 1, 0, 0, 0, 11, 353, 1, 0, 0, 0, 11, 355, 1, 0, 0, 0, 11, 357, 1, 0, 0, 0, 11, 359, 1, 0, 0, 0, 11, 361, 1, 0, 0, 0, 11, 363, 1, 0, 0, 0, 11, 365, 1, 0, 0, 0, 12, 367, 1, 0, 0, 0, 12, 369, 1, 0, 0, 0, 12, 371, 1, 0, 0, 0, 12, 373, 1, 0, 0, 0, 12, 375, 1, 0, 0, 0, 12, 377, 1, 0, 0, 0, 12, 379, 1, 0, 0, 0, 13, 381, 1, 0, 0, 0, 13, 383, 1, 0, 0, 0, 13, 385, 1, 0, 0, 0, 13, 387, 1, 0, 0, 0, 13, 389, 1, 0, 0, 0, 13, 391, 1, 0, 0, 0, 14, 393, 1, 0, 0, 0, 14, 395, 1, 0, 0, 0, 14, 397, 1, 0, 0, 0, 14, 399, 1, 0, 0, 0, 14, 401, 1, 0, 0, 0, 14, 403, 1, 0, 0, 0, 14, 405, 1, 0, 0, 0, 14, 407, 1, 0, 0, 0, 14, 409, 1, 0, 0, 0, 15, 411, 1, 0, 0, 0, 17, 421, 1, 0, 0, 0, 19, 428, 1, 0, 0, 0, 21, 437, 1, 0, 0, 0, 23, 444, 1, 0, 0, 0, 25, 454, 1, 0, 0, 0, 27, 461, 1, 0, 0, 0, 29, 468, 1, 0, 0, 0, 31, 475, 1, 0, 0, 0, 33, 483, 1, 0, 0, 0, 35, 495, 1, 0, 0, 0, 37, 504, 1, 0, 0, 0, 39, 510, 1, 0, 0, 0, 41, 517, 1, 0, 0, 0, 43, 524, 1, 0, 0, 0, 45, 532, 1, 0, 0, 0, 47, 540, 1, 0, 0, 0, 49, 555, 1, 0, 0, 0, 51, 565, 1, 0, 0, 0, 53, 577, 1, 0, 0, 0, 55, 583, 1, 0, 0, 0, 57, 600, 1, 0, 0, 0, 59, 616, 1, 0, 0, 0, 61, 622, 1, 0, 0, 0, 63, 626, 1, 0, 0, 0, 65, 628, 1, 0, 0, 0, 67, 630, 1, 0, 0, 0, 69, 633, 1, 0, 0, 0, 71, 635, 1, 0, 0, 0, 73, 644, 1, 0, 0, 0, 75, 646, 1, 0, 0, 0, 77, 651, 1, 0, 0, 0, 79, 653, 1, 0, 0, 0, 81, 658, 1, 0, 0, 0, 83, 689, 1, 0, 0, 0, 85, 692, 1, 0, 0, 0, 87, 738, 1, 0, 0, 0, 89, 740, 1, 0, 0, 0, 91, 743, 1, 0, 0, 0, 93, 747, 1, 0, 0, 0, 95, 751, 1, 0, 0, 0, 97, 753, 1, 0, 0, 0, 99, 756, 1, 0, 0, 0, 101, 758, 1, 0, 0, 0, 103, 763, 1, 0, 0, 0, 105, 765, 1, 0, 0, 0, 107, 771, 1, 0, 0, 0, 109, 777, 1, 0, 0, 0, 111, 780, 1, 0, 0, 0, 113, 783, 1, 0, 0, 0, 115, 788, 1, 0, 0, 0, 117, 793, 1, 0, 0, 0, 119, 795, 1, 0, 0, 0, 121, 799, 1, 0, 0, 0, 123, 804, 1, 0, 0, 0, 125, 810, 1, 0, 0, 0, 127, 813, 1, 0, 0, 0, 129, 815, 1, 0, 0, 0, 131, 821, 1, 0, 0, 0, 133, 823, 1, 0, 0, 0, 135, 828, 1, 0, 0, 0, 137, 831, 1, 0, 0, 0, 139, 834, 1, 0, 0, 0, 141, 837, 1, 0, 0, 0, 143, 839, 1, 0, 0, 0, 145, 842, 1, 0, 0, 0, 147, 844, 1, 0, 0, 0, 149, 847, 1, 0, 0, 0, 151, 849, 1, 0, 0, 0, 153, 851, 1, 0, 0, 0, 155, 853, 1, 0, 0, 0, 157, 855, 1, 0, 0, 0, 159, 857, 1, 0, 0, 0, 161, 863, 1, 0, 0, 0, 163, 884, 1, 0, 0, 0, 165, 886, 1, 0, 0, 0, 167, 891, 1, 0, 0, 0, 169, 912, 1, 0, 0, 0, 171, 914, 1, 0, 0, 0, 173, 922, 1, 0, 0, 0, 175, 924, 1, 0, 0, 0, 177, 928, 1, 0, 0, 0, 179, 932, 1, 0, 0, 0, 181, 936, 1, 0, 0, 0, 183, 941, 1, 0, 0, 0, 185, 946, 1, 0, 0, 0, 187, 950, 1, 0, 0, 0, 189, 954, 1, 0, 0, 0, 191, 958, 1, 0, 0, 0, 193, 963, 1, 0, 0, 0, 195, 967, 1, 0, 0, 0, 197, 971, 1, 0, 0, 0, 199, 975, 1, 0, 0, 0, 201, 979, 1, 0, 0, 0, 203, 983, 1, 0, 0, 0, 205, 995, 1, 0, 0, 0, 207, 998, 1, 0, 0, 0, 209, 1002, 1, 0, 0, 0, 211, 1006, 1, 0, 0, 0, 213, 1010, 1, 0, 0, 0, 215, 1014, 1, 0, 0, 0, 217, 1018, 1, 0, 0, 0, 219, 1022, 1, 0, 0, 0, 221, 1027, 1, 0, 0, 0, 223, 1031, 1, 0, 0, 0, 225, 1035, 1, 0, 0, 0, 227, 1040, 1, 0, 0, 0, 229, 1049, 1, 0, 0, 0, 231, 1070, 1, 0, 0, 0, 233, 1074, 1, 0, 0, 0, 235, 1078, 1, 0, 0, 0, 237, 1082, 1, 0, 0, 0, 239, 1086, 1, 0, 0, 0, 241, 1090, 1, 0, 0, 0, 243, 1095, 1, 0, 0, 0, 245, 1099, 1, 0, 0, 0, 247, 1103, 1, 0, 0, 0, 249, 1107, 1, 0, 0, 0, 251, 1112, 1, 0, 0, 0, 253, 1117, 1, 0, 0, 0, 255, 1120, 1, 0, 0, 0, 257, 1124, 1, 0, 0, 0, 259, 1128, 1, 0, 0, 0, 261, 1132, 1, 0, 0, 0, 263, 1136, 1, 0, 0, 0, 265, 1141, 1, 0, 0, 0, 267, 1146, 1, 0, 0, 0, 269, 1151, 1, 0, 0, 0, 271, 1158, 1, 0, 0, 0, 273, 1167, 1, 0, 0, 0, 275, 1174, 1, 0, 0, 0, 277, 1178, 1, 0, 0, 0, 279, 1182, 1, 0, 0, 0, 281, 1186, 1, 0, 0, 0, 283, 1190, 1, 0, 0, 0, 285, 1196, 1, 0, 0, 0, 287, 1200, 1, 0, 0, 0, 289, 1204, 1, 0, 0, 0, 291, 1208, 1, 0, 0, 0, 293, 1212, 1, 0, 0, 0, 295, 1216, 1, 0, 0, 0, 297, 1220, 1, 0, 0, 0, 299, 1225, 1, 0, 0, 0, 301, 1230, 1, 0, 0, 0, 303, 1234, 1, 0, 0, 0, 305, 1238, 1, 0, 0, 0, 307, 1242, 1, 0, 0, 0, 309, 1247, 1, 0, 0, 0, 311, 1251, 1, 0, 0, 0, 313, 1256, 1, 0, 0, 0, 315, 1261, 1, 0, 0, 0, 317, 1265, 1, 0, 0, 0, 319, 1269, 1, 0, 0, 0, 321, 1273, 1, 0, 0, 0, 323, 1277, 1, 0, 0, 0, 325, 1281, 1, 0, 0, 0, 327, 1286, 1, 0, 0, 0, 329, 1291, 1, 0, 0, 0, 331, 1295, 1, 0, 0, 0, 333, 1299, 1, 0, 0, 0, 335, 1303, 1, 0, 0, 0, 337, 1308, 1, 0, 0, 0, 339, 1315, 1, 0, 0, 0, 341, 1319, 1, 0, 0, 0, 343, 1323, 1, 0, 0, 0, 345, 1327, 1, 0, 0, 0, 347, 1331, 1, 0, 0, 0, 349, 1336, 1, 0, 0, 0, 351, 1340, 1, 0, 0, 0, 353, 1344, 1, 0, 0, 0, 355, 1348, 1, 0, 0, 0, 357, 1353, 1, 0, 0, 0, 359, 1357, 1, 0, 0, 0, 361, 1361, 1, 0, 0, 0, 363, 1365, 1, 0, 0, 0, 365, 1369, 1, 0, 0, 0, 367, 1373, 1, 0, 0, 0, 369, 1379, 1, 0, 0, 0, 371, 1383, 1, 0, 0, 0, 373, 1387, 1, 0, 0, 0, 375, 1391, 1, 0, 0, 0, 377, 1395, 1, 0, 0, 0, 379, 1399, 1, 0, 0, 0, 381, 1403, 1, 0, 0, 0, 383, 1408, 1, 0, 0, 0, 385, 1414, 1, 0, 0, 0, 387, 1420, 1, 0, 0, 0, 389, 1424, 1, 0, 0, 0, 391, 1428, 1, 0, 0, 0, 393, 1432, 1, 0, 0, 0, 395, 1438, 1, 0, 0, 0, 397, 1444, 1, 0, 0, 0, 399, 1448, 1, 0, 0, 0, 401, 1452, 1, 0, 0, 0, 403, 1456, 1, 0, 0, 0, 405, 1462, 1, 0, 0, 0, 407, 1468, 1, 0, 0, 0, 409, 1474, 1, 0, 0, 0, 411, 412, 7, 0, 0, 0, 412, 413, 7, 1, 0, 0, 413, 414, 7, 2, 0, 0, 414, 415, 7, 2, 0, 0, 415, 416, 7, 3, 0, 0, 416, 417, 7, 4, 0, 0, 417, 418, 7, 5, 0, 0, 418, 419, 1, 0, 0, 0, 419, 420, 6, 0, 0, 0, 420, 16, 1, 0, 0, 0, 421, 422, 7, 0, 0, 0, 422, 423, 7, 6, 0, 0, 423, 424, 7, 7, 0, 0, 424, 425, 7, 8, 0, 0, 425, 426, 1, 0, 0, 0, 426, 427, 6, 1, 1, 0, 427, 18, 1, 0, 0, 0, 428, 429, 7, 3, 0, 0, 429, 430, 7, 9, 0, 0, 430, 431, 7, 6, 0, 0, 431, 432, 7, 1, 0, 0, 432, 433, 7, 4, 0, 0, 433, 434, 7, 10, 0, 0, 434, 435, 1, 0, 0, 0, 435, 436, 6, 2, 2, 0, 436, 20, 1, 0, 0, 0, 437, 438, 7, 3, 0, 0, 438, 439, 7, 11, 0, 0, 439, 440, 7, 12, 0, 0, 440, 441, 7, 13, 0, 0, 441, 442, 1, 0, 0, 0, 442, 443, 6, 3, 0, 0, 443, 22, 1, 0, 0, 0, 444, 445, 7, 3, 0, 0, 445, 446, 7, 14, 0, 0, 446, 447, 7, 8, 0, 0, 447, 448, 7, 13, 0, 0, 448, 449, 7, 12, 0, 0, 449, 450, 7, 1, 0, 0, 450, 451, 7, 9, 0, 0, 451, 452, 1, 0, 0, 0, 452, 453, 6, 4, 3, 0, 453, 24, 1, 0, 0, 0, 454, 455, 7, 15, 0, 0, 455, 456, 7, 6, 0, 0, 456, 457, 7, 7, 0, 0, 457, 458, 7, 16, 0, 0, 458, 459, 1, 0, 0, 0, 459, 460, 6, 5, 4, 0, 460, 26, 1, 0, 0, 0, 461, 462, 7, 17, 0, 0, 462, 463, 7, 6, 0, 0, 463, 464, 7, 7, 0, 0, 464, 465, 7, 18, 0, 0, 465, 466, 1, 0, 0, 0, 466, 467, 6, 6, 0, 0, 467, 28, 1, 0, 0, 0, 468, 469, 7, 18, 0, 0, 469, 470, 7, 3, 0, 0, 470, 471, 7, 3, 0, 0, 471, 472, 7, 8, 0, 0, 472, 473, 1, 0, 0, 0, 473, 474, 6, 7, 1, 0, 474, 30, 1, 0, 0, 0, 475, 476, 7, 13, 0, 0, 476, 477, 7, 1, 0, 0, 477, 478, 7, 16, 0, 0, 478, 479, 7, 1, 0, 0, 479, 480, 7, 5, 0, 0, 480, 481, 1, 0, 0, 0, 481, 482, 6, 8, 0, 0, 482, 32, 1, 0, 0, 0, 483, 484, 7, 16, 0, 0, 484, 485, 7, 11, 0, 0, 485, 486, 5, 95, 0, 0, 486, 487, 7, 3, 0, 0, 487, 488, 7, 14, 0, 0, 488, 489, 7, 8, 0, 0, 489, 490, 7, 12, 0, 0, 490, 491, 7, 9, 0, 0, 491, 492, 7, 0, 0, 0, 492, 493, 1, 0, 0, 0, 493, 494, 6, 9, 5, 0, 494, 34, 1, 0, 0, 0, 495, 496, 7, 6, 0, 0, 496, 497, 7, 3, 0, 0, 497, 498, 7, 9, 0, 0, 498, 499, 7, 12, 0, 0, 499, 500, 7, 16, 0, 0, 500, 501, 7, 3, 0, 0, 501, 502, 1, 0, 0, 0, 502, 503, 6, 10, 6, 0, 503, 36, 1, 0, 0, 0, 504, 505, 7, 6, 0, 0, 505, 506, 7, 7, 0, 0, 506, 507, 7, 19, 0, 0, 507, 508, 1, 0, 0, 0, 508, 509, 6, 11, 0, 0, 509, 38, 1, 0, 0, 0, 510, 511, 7, 2, 0, 0, 511, 512, 7, 10, 0, 0, 512, 513, 7, 7, 0, 0, 513, 514, 7, 19, 0, 0, 514, 515, 1, 0, 0, 0, 515, 516, 6, 12, 7, 0, 516, 40, 1, 0, 0, 0, 517, 518, 7, 2, 0, 0, 518, 519, 7, 7, 0, 0, 519, 520, 7, 6, 0, 0, 520, 521, 7, 5, 0, 0, 521, 522, 1, 0, 0, 0, 522, 523, 6, 13, 0, 0, 523, 42, 1, 0, 0, 0, 524, 525, 7, 2, 0, 0, 525, 526, 7, 5, 0, 0, 526, 527, 7, 12, 0, 0, 527, 528, 7, 5, 0, 0, 528, 529, 7, 2, 0, 0, 529, 530, 1, 0, 0, 0, 530, 531, 6, 14, 0, 0, 531, 44, 1, 0, 0, 0, 532, 533, 7, 19, 0, 0, 533, 534, 7, 10, 0, 0, 534, 535, 7, 3, 0, 0, 535, 536, 7, 6, 0, 0, 536, 537, 7, 3, 0, 0, 537, 538, 1, 0, 0, 0, 538, 539, 6, 15, 0, 0, 539, 46, 1, 0, 0, 0, 540, 541, 4, 16, 0, 0, 541, 542, 7, 1, 0, 0, 542, 543, 7, 9, 0, 0, 543, 544, 7, 13, 0, 0, 544, 545, 7, 1, 0, 0, 545, 546, 7, 9, 0, 0, 546, 547, 7, 3, 0, 0, 547, 548, 7, 2, 0, 0, 548, 549, 7, 5, 0, 0, 549, 550, 7, 12, 0, 0, 550, 551, 7, 5, 0, 0, 551, 552, 7, 2, 0, 0, 552, 553, 1, 0, 0, 0, 553, 554, 6, 16, 0, 0, 554, 48, 1, 0, 0, 0, 555, 556, 4, 17, 1, 0, 556, 557, 7, 13, 0, 0, 557, 558, 7, 7, 0, 0, 558, 559, 7, 7, 0, 0, 559, 560, 7, 18, 0, 0, 560, 561, 7, 20, 0, 0, 561, 562, 7, 8, 0, 0, 562, 563, 1, 0, 0, 0, 563, 564, 6, 17, 8, 0, 564, 50, 1, 0, 0, 0, 565, 566, 4, 18, 2, 0, 566, 567, 7, 16, 0, 0, 567, 568, 7, 3, 0, 0, 568, 569, 7, 5, 0, 0, 569, 570, 7, 6, 0, 0, 570, 571, 7, 1, 0, 0, 571, 572, 7, 4, 0, 0, 572, 573, 7, 2, 0, 0, 573, 574, 1, 0, 0, 0, 574, 575, 6, 18, 9, 0, 575, 52, 1, 0, 0, 0, 576, 578, 8, 21, 0, 0, 577, 576, 1, 0, 0, 0, 578, 579, 1, 0, 0, 0, 579, 577, 1, 0, 0, 0, 579, 580, 1, 0, 0, 0, 580, 581, 1, 0, 0, 0, 581, 582, 6, 19, 0, 0, 582, 54, 1, 0, 0, 0, 583, 584, 5, 47, 0, 0, 584, 585, 5, 47, 0, 0, 585, 589, 1, 0, 0, 0, 586, 588, 8, 22, 0, 0, 587, 586, 1, 0, 0, 0, 588, 591, 1, 0, 0, 0, 589, 587, 1, 0, 0, 0, 589, 590, 1, 0, 0, 0, 590, 593, 1, 0, 0, 0, 591, 589, 1, 0, 0, 0, 592, 594, 5, 13, 0, 0, 593, 592, 1, 0, 0, 0, 593, 594, 1, 0, 0, 0, 594, 596, 1, 0, 0, 0, 595, 597, 5, 10, 0, 0, 596, 595, 1, 0, 0, 0, 596, 597, 1, 0, 0, 0, 597, 598, 1, 0, 0, 0, 598, 599, 6, 20, 10, 0, 599, 56, 1, 0, 0, 0, 600, 601, 5, 47, 0, 0, 601, 602, 5, 42, 0, 0, 602, 607, 1, 0, 0, 0, 603, 606, 3, 57, 21, 0, 604, 606, 9, 0, 0, 0, 605, 603, 1, 0, 0, 0, 605, 604, 1, 0, 0, 0, 606, 609, 1, 0, 0, 0, 607, 608, 1, 0, 0, 0, 607, 605, 1, 0, 0, 0, 608, 610, 1, 0, 0, 0, 609, 607, 1, 0, 0, 0, 610, 611, 5, 42, 0, 0, 611, 612, 5, 47, 0, 0, 612, 613, 1, 0, 0, 0, 613, 614, 6, 21, 10, 0, 614, 58, 1, 0, 0, 0, 615, 617, 7, 23, 0, 0, 616, 615, 1, 0, 0, 0, 617, 618, 1, 0, 0, 0, 618, 616, 1, 0, 0, 0, 618, 619, 1, 0, 0, 0, 619, 620, 1, 0, 0, 0, 620, 621, 6, 22, 10, 0, 621, 60, 1, 0, 0, 0, 622, 623, 5, 124, 0, 0, 623, 624, 1, 0, 0, 0, 624, 625, 6, 23, 11, 0, 625, 62, 1, 0, 0, 0, 626, 627, 7, 24, 0, 0, 627, 64, 1, 0, 0, 0, 628, 629, 7, 25, 0, 0, 629, 66, 1, 0, 0, 0, 630, 631, 5, 92, 0, 0, 631, 632, 7, 26, 0, 0, 632, 68, 1, 0, 0, 0, 633, 634, 8, 27, 0, 0, 634, 70, 1, 0, 0, 0, 635, 637, 7, 3, 0, 0, 636, 638, 7, 28, 0, 0, 637, 636, 1, 0, 0, 0, 637, 638, 1, 0, 0, 0, 638, 640, 1, 0, 0, 0, 639, 641, 3, 63, 24, 0, 640, 639, 1, 0, 0, 0, 641, 642, 1, 0, 0, 0, 642, 640, 1, 0, 0, 0, 642, 643, 1, 0, 0, 0, 643, 72, 1, 0, 0, 0, 644, 645, 5, 64, 0, 0, 645, 74, 1, 0, 0, 0, 646, 647, 5, 96, 0, 0, 647, 76, 1, 0, 0, 0, 648, 652, 8, 29, 0, 0, 649, 650, 5, 96, 0, 0, 650, 652, 5, 96, 0, 0, 651, 648, 1, 0, 0, 0, 651, 649, 1, 0, 0, 0, 652, 78, 1, 0, 0, 0, 653, 654, 5, 95, 0, 0, 654, 80, 1, 0, 0, 0, 655, 659, 3, 65, 25, 0, 656, 659, 3, 63, 24, 0, 657, 659, 3, 79, 32, 0, 658, 655, 1, 0, 0, 0, 658, 656, 1, 0, 0, 0, 658, 657, 1, 0, 0, 0, 659, 82, 1, 0, 0, 0, 660, 665, 5, 34, 0, 0, 661, 664, 3, 67, 26, 0, 662, 664, 3, 69, 27, 0, 663, 661, 1, 0, 0, 0, 663, 662, 1, 0, 0, 0, 664, 667, 1, 0, 0, 0, 665, 663, 1, 0, 0, 0, 665, 666, 1, 0, 0, 0, 666, 668, 1, 0, 0, 0, 667, 665, 1, 0, 0, 0, 668, 690, 5, 34, 0, 0, 669, 670, 5, 34, 0, 0, 670, 671, 5, 34, 0, 0, 671, 672, 5, 34, 0, 0, 672, 676, 1, 0, 0, 0, 673, 675, 8, 22, 0, 0, 674, 673, 1, 0, 0, 0, 675, 678, 1, 0, 0, 0, 676, 677, 1, 0, 0, 0, 676, 674, 1, 0, 0, 0, 677, 679, 1, 0, 0, 0, 678, 676, 1, 0, 0, 0, 679, 680, 5, 34, 0, 0, 680, 681, 5, 34, 0, 0, 681, 682, 5, 34, 0, 0, 682, 684, 1, 0, 0, 0, 683, 685, 5, 34, 0, 0, 684, 683, 1, 0, 0, 0, 684, 685, 1, 0, 0, 0, 685, 687, 1, 0, 0, 0, 686, 688, 5, 34, 0, 0, 687, 686, 1, 0, 0, 0, 687, 688, 1, 0, 0, 0, 688, 690, 1, 0, 0, 0, 689, 660, 1, 0, 0, 0, 689, 669, 1, 0, 0, 0, 690, 84, 1, 0, 0, 0, 691, 693, 3, 63, 24, 0, 692, 691, 1, 0, 0, 0, 693, 694, 1, 0, 0, 0, 694, 692, 1, 0, 0, 0, 694, 695, 1, 0, 0, 0, 695, 86, 1, 0, 0, 0, 696, 698, 3, 63, 24, 0, 697, 696, 1, 0, 0, 0, 698, 699, 1, 0, 0, 0, 699, 697, 1, 0, 0, 0, 699, 700, 1, 0, 0, 0, 700, 701, 1, 0, 0, 0, 701, 705, 3, 103, 44, 0, 702, 704, 3, 63, 24, 0, 703, 702, 1, 0, 0, 0, 704, 707, 1, 0, 0, 0, 705, 703, 1, 0, 0, 0, 705, 706, 1, 0, 0, 0, 706, 739, 1, 0, 0, 0, 707, 705, 1, 0, 0, 0, 708, 710, 3, 103, 44, 0, 709, 711, 3, 63, 24, 0, 710, 709, 1, 0, 0, 0, 711, 712, 1, 0, 0, 0, 712, 710, 1, 0, 0, 0, 712, 713, 1, 0, 0, 0, 713, 739, 1, 0, 0, 0, 714, 716, 3, 63, 24, 0, 715, 714, 1, 0, 0, 0, 716, 717, 1, 0, 0, 0, 717, 715, 1, 0, 0, 0, 717, 718, 1, 0, 0, 0, 718, 726, 1, 0, 0, 0, 719, 723, 3, 103, 44, 0, 720, 722, 3, 63, 24, 0, 721, 720, 1, 0, 0, 0, 722, 725, 1, 0, 0, 0, 723, 721, 1, 0, 0, 0, 723, 724, 1, 0, 0, 0, 724, 727, 1, 0, 0, 0, 725, 723, 1, 0, 0, 0, 726, 719, 1, 0, 0, 0, 726, 727, 1, 0, 0, 0, 727, 728, 1, 0, 0, 0, 728, 729, 3, 71, 28, 0, 729, 739, 1, 0, 0, 0, 730, 732, 3, 103, 44, 0, 731, 733, 3, 63, 24, 0, 732, 731, 1, 0, 0, 0, 733, 734, 1, 0, 0, 0, 734, 732, 1, 0, 0, 0, 734, 735, 1, 0, 0, 0, 735, 736, 1, 0, 0, 0, 736, 737, 3, 71, 28, 0, 737, 739, 1, 0, 0, 0, 738, 697, 1, 0, 0, 0, 738, 708, 1, 0, 0, 0, 738, 715, 1, 0, 0, 0, 738, 730, 1, 0, 0, 0, 739, 88, 1, 0, 0, 0, 740, 741, 7, 30, 0, 0, 741, 742, 7, 31, 0, 0, 742, 90, 1, 0, 0, 0, 743, 744, 7, 12, 0, 0, 744, 745, 7, 9, 0, 0, 745, 746, 7, 0, 0, 0, 746, 92, 1, 0, 0, 0, 747, 748, 7, 12, 0, 0, 748, 749, 7, 2, 0, 0, 749, 750, 7, 4, 0, 0, 750, 94, 1, 0, 0, 0, 751, 752, 5, 61, 0, 0, 752, 96, 1, 0, 0, 0, 753, 754, 5, 58, 0, 0, 754, 755, 5, 58, 0, 0, 755, 98, 1, 0, 0, 0, 756, 757, 5, 44, 0, 0, 757, 100, 1, 0, 0, 0, 758, 759, 7, 0, 0, 0, 759, 760, 7, 3, 0, 0, 760, 761, 7, 2, 0, 0, 761, 762, 7, 4, 0, 0, 762, 102, 1, 0, 0, 0, 763, 764, 5, 46, 0, 0, 764, 104, 1, 0, 0, 0, 765, 766, 7, 15, 0, 0, 766, 767, 7, 12, 0, 0, 767, 768, 7, 13, 0, 0, 768, 769, 7, 2, 0, 0, 769, 770, 7, 3, 0, 0, 770, 106, 1, 0, 0, 0, 771, 772, 7, 15, 0, 0, 772, 773, 7, 1, 0, 0, 773, 774, 7, 6, 0, 0, 774, 775, 7, 2, 0, 0, 775, 776, 7, 5, 0, 0, 776, 108, 1, 0, 0, 0, 777, 778, 7, 1, 0, 0, 778, 779, 7, 9, 0, 0, 779, 110, 1, 0, 0, 0, 780, 781, 7, 1, 0, 0, 781, 782, 7, 2, 0, 0, 782, 112, 1, 0, 0, 0, 783, 784, 7, 13, 0, 0, 784, 785, 7, 12, 0, 0, 785, 786, 7, 2, 0, 0, 786, 787, 7, 5, 0, 0, 787, 114, 1, 0, 0, 0, 788, 789, 7, 13, 0, 0, 789, 790, 7, 1, 0, 0, 790, 791, 7, 18, 0, 0, 791, 792, 7, 3, 0, 0, 792, 116, 1, 0, 0, 0, 793, 794, 5, 40, 0, 0, 794, 118, 1, 0, 0, 0, 795, 796, 7, 9, 0, 0, 796, 797, 7, 7, 0, 0, 797, 798, 7, 5, 0, 0, 798, 120, 1, 0, 0, 0, 799, 800, 7, 9, 0, 0, 800, 801, 7, 20, 0, 0, 801, 802, 7, 13, 0, 0, 802, 803, 7, 13, 0, 0, 803, 122, 1, 0, 0, 0, 804, 805, 7, 9, 0, 0, 805, 806, 7, 20, 0, 0, 806, 807, 7, 13, 0, 0, 807, 808, 7, 13, 0, 0, 808, 809, 7, 2, 0, 0, 809, 124, 1, 0, 0, 0, 810, 811, 7, 7, 0, 0, 811, 812, 7, 6, 0, 0, 812, 126, 1, 0, 0, 0, 813, 814, 5, 63, 0, 0, 814, 128, 1, 0, 0, 0, 815, 816, 7, 6, 0, 0, 816, 817, 7, 13, 0, 0, 817, 818, 7, 1, 0, 0, 818, 819, 7, 18, 0, 0, 819, 820, 7, 3, 0, 0, 820, 130, 1, 0, 0, 0, 821, 822, 5, 41, 0, 0, 822, 132, 1, 0, 0, 0, 823, 824, 7, 5, 0, 0, 824, 825, 7, 6, 0, 0, 825, 826, 7, 20, 0, 0, 826, 827, 7, 3, 0, 0, 827, 134, 1, 0, 0, 0, 828, 829, 5, 61, 0, 0, 829, 830, 5, 61, 0, 0, 830, 136, 1, 0, 0, 0, 831, 832, 5, 61, 0, 0, 832, 833, 5, 126, 0, 0, 833, 138, 1, 0, 0, 0, 834, 835, 5, 33, 0, 0, 835, 836, 5, 61, 0, 0, 836, 140, 1, 0, 0, 0, 837, 838, 5, 60, 0, 0, 838, 142, 1, 0, 0, 0, 839, 840, 5, 60, 0, 0, 840, 841, 5, 61, 0, 0, 841, 144, 1, 0, 0, 0, 842, 843, 5, 62, 0, 0, 843, 146, 1, 0, 0, 0, 844, 845, 5, 62, 0, 0, 845, 846, 5, 61, 0, 0, 846, 148, 1, 0, 0, 0, 847, 848, 5, 43, 0, 0, 848, 150, 1, 0, 0, 0, 849, 850, 5, 45, 0, 0, 850, 152, 1, 0, 0, 0, 851, 852, 5, 42, 0, 0, 852, 154, 1, 0, 0, 0, 853, 854, 5, 47, 0, 0, 854, 156, 1, 0, 0, 0, 855, 856, 5, 37, 0, 0, 856, 158, 1, 0, 0, 0, 857, 858, 7, 16, 0, 0, 858, 859, 7, 12, 0, 0, 859, 860, 7, 5, 0, 0, 860, 861, 7, 4, 0, 0, 861, 862, 7, 10, 0, 0, 862, 160, 1, 0, 0, 0, 863, 864, 3, 45, 15, 0, 864, 865, 1, 0, 0, 0, 865, 866, 6, 73, 12, 0, 866, 162, 1, 0, 0, 0, 867, 870, 3, 127, 56, 0, 868, 871, 3, 65, 25, 0, 869, 871, 3, 79, 32, 0, 870, 868, 1, 0, 0, 0, 870, 869, 1, 0, 0, 0, 871, 875, 1, 0, 0, 0, 872, 874, 3, 81, 33, 0, 873, 872, 1, 0, 0, 0, 874, 877, 1, 0, 0, 0, 875, 873, 1, 0, 0, 0, 875, 876, 1, 0, 0, 0, 876, 885, 1, 0, 0, 0, 877, 875, 1, 0, 0, 0, 878, 880, 3, 127, 56, 0, 879, 881, 3, 63, 24, 0, 880, 879, 1, 0, 0, 0, 881, 882, 1, 0, 0, 0, 882, 880, 1, 0, 0, 0, 882, 883, 1, 0, 0, 0, 883, 885, 1, 0, 0, 0, 884, 867, 1, 0, 0, 0, 884, 878, 1, 0, 0, 0, 885, 164, 1, 0, 0, 0, 886, 887, 5, 91, 0, 0, 887, 888, 1, 0, 0, 0, 888, 889, 6, 75, 0, 0, 889, 890, 6, 75, 0, 0, 890, 166, 1, 0, 0, 0, 891, 892, 5, 93, 0, 0, 892, 893, 1, 0, 0, 0, 893, 894, 6, 76, 11, 0, 894, 895, 6, 76, 11, 0, 895, 168, 1, 0, 0, 0, 896, 900, 3, 65, 25, 0, 897, 899, 3, 81, 33, 0, 898, 897, 1, 0, 0, 0, 899, 902, 1, 0, 0, 0, 900, 898, 1, 0, 0, 0, 900, 901, 1, 0, 0, 0, 901, 913, 1, 0, 0, 0, 902, 900, 1, 0, 0, 0, 903, 906, 3, 79, 32, 0, 904, 906, 3, 73, 29, 0, 905, 903, 1, 0, 0, 0, 905, 904, 1, 0, 0, 0, 906, 908, 1, 0, 0, 0, 907, 909, 3, 81, 33, 0, 908, 907, 1, 0, 0, 0, 909, 910, 1, 0, 0, 0, 910, 908, 1, 0, 0, 0, 910, 911, 1, 0, 0, 0, 911, 913, 1, 0, 0, 0, 912, 896, 1, 0, 0, 0, 912, 905, 1, 0, 0, 0, 913, 170, 1, 0, 0, 0, 914, 916, 3, 75, 30, 0, 915, 917, 3, 77, 31, 0, 916, 915, 1, 0, 0, 0, 917, 918, 1, 0, 0, 0, 918, 916, 1, 0, 0, 0, 918, 919, 1, 0, 0, 0, 919, 920, 1, 0, 0, 0, 920, 921, 3, 75, 30, 0, 921, 172, 1, 0, 0, 0, 922, 923, 3, 171, 78, 0, 923, 174, 1, 0, 0, 0, 924, 925, 3, 55, 20, 0, 925, 926, 1, 0, 0, 0, 926, 927, 6, 80, 10, 0, 927, 176, 1, 0, 0, 0, 928, 929, 3, 57, 21, 0, 929, 930, 1, 0, 0, 0, 930, 931, 6, 81, 10, 0, 931, 178, 1, 0, 0, 0, 932, 933, 3, 59, 22, 0, 933, 934, 1, 0, 0, 0, 934, 935, 6, 82, 10, 0, 935, 180, 1, 0, 0, 0, 936, 937, 3, 165, 75, 0, 937, 938, 1, 0, 0, 0, 938, 939, 6, 83, 13, 0, 939, 940, 6, 83, 14, 0, 940, 182, 1, 0, 0, 0, 941, 942, 3, 61, 23, 0, 942, 943, 1, 0, 0, 0, 943, 944, 6, 84, 15, 0, 944, 945, 6, 84, 11, 0, 945, 184, 1, 0, 0, 0, 946, 947, 3, 59, 22, 0, 947, 948, 1, 0, 0, 0, 948, 949, 6, 85, 10, 0, 949, 186, 1, 0, 0, 0, 950, 951, 3, 55, 20, 0, 951, 952, 1, 0, 0, 0, 952, 953, 6, 86, 10, 0, 953, 188, 1, 0, 0, 0, 954, 955, 3, 57, 21, 0, 955, 956, 1, 0, 0, 0, 956, 957, 6, 87, 10, 0, 957, 190, 1, 0, 0, 0, 958, 959, 3, 61, 23, 0, 959, 960, 1, 0, 0, 0, 960, 961, 6, 88, 15, 0, 961, 962, 6, 88, 11, 0, 962, 192, 1, 0, 0, 0, 963, 964, 3, 165, 75, 0, 964, 965, 1, 0, 0, 0, 965, 966, 6, 89, 13, 0, 966, 194, 1, 0, 0, 0, 967, 968, 3, 167, 76, 0, 968, 969, 1, 0, 0, 0, 969, 970, 6, 90, 16, 0, 970, 196, 1, 0, 0, 0, 971, 972, 3, 337, 161, 0, 972, 973, 1, 0, 0, 0, 973, 974, 6, 91, 17, 0, 974, 198, 1, 0, 0, 0, 975, 976, 3, 99, 42, 0, 976, 977, 1, 0, 0, 0, 977, 978, 6, 92, 18, 0, 978, 200, 1, 0, 0, 0, 979, 980, 3, 95, 40, 0, 980, 981, 1, 0, 0, 0, 981, 982, 6, 93, 19, 0, 982, 202, 1, 0, 0, 0, 983, 984, 7, 16, 0, 0, 984, 985, 7, 3, 0, 0, 985, 986, 7, 5, 0, 0, 986, 987, 7, 12, 0, 0, 987, 988, 7, 0, 0, 0, 988, 989, 7, 12, 0, 0, 989, 990, 7, 5, 0, 0, 990, 991, 7, 12, 0, 0, 991, 204, 1, 0, 0, 0, 992, 996, 8, 32, 0, 0, 993, 994, 5, 47, 0, 0, 994, 996, 8, 33, 0, 0, 995, 992, 1, 0, 0, 0, 995, 993, 1, 0, 0, 0, 996, 206, 1, 0, 0, 0, 997, 999, 3, 205, 95, 0, 998, 997, 1, 0, 0, 0, 999, 1000, 1, 0, 0, 0, 1000, 998, 1, 0, 0, 0, 1000, 1001, 1, 0, 0, 0, 1001, 208, 1, 0, 0, 0, 1002, 1003, 3, 207, 96, 0, 1003, 1004, 1, 0, 0, 0, 1004, 1005, 6, 97, 20, 0, 1005, 210, 1, 0, 0, 0, 1006, 1007, 3, 83, 34, 0, 1007, 1008, 1, 0, 0, 0, 1008, 1009, 6, 98, 21, 0, 1009, 212, 1, 0, 0, 0, 1010, 1011, 3, 55, 20, 0, 1011, 1012, 1, 0, 0, 0, 1012, 1013, 6, 99, 10, 0, 1013, 214, 1, 0, 0, 0, 1014, 1015, 3, 57, 21, 0, 1015, 1016, 1, 0, 0, 0, 1016, 1017, 6, 100, 10, 0, 1017, 216, 1, 0, 0, 0, 1018, 1019, 3, 59, 22, 0, 1019, 1020, 1, 0, 0, 0, 1020, 1021, 6, 101, 10, 0, 1021, 218, 1, 0, 0, 0, 1022, 1023, 3, 61, 23, 0, 1023, 1024, 1, 0, 0, 0, 1024, 1025, 6, 102, 15, 0, 1025, 1026, 6, 102, 11, 0, 1026, 220, 1, 0, 0, 0, 1027, 1028, 3, 103, 44, 0, 1028, 1029, 1, 0, 0, 0, 1029, 1030, 6, 103, 22, 0, 1030, 222, 1, 0, 0, 0, 1031, 1032, 3, 99, 42, 0, 1032, 1033, 1, 0, 0, 0, 1033, 1034, 6, 104, 18, 0, 1034, 224, 1, 0, 0, 0, 1035, 1036, 4, 105, 3, 0, 1036, 1037, 3, 127, 56, 0, 1037, 1038, 1, 0, 0, 0, 1038, 1039, 6, 105, 23, 0, 1039, 226, 1, 0, 0, 0, 1040, 1041, 4, 106, 4, 0, 1041, 1042, 3, 163, 74, 0, 1042, 1043, 1, 0, 0, 0, 1043, 1044, 6, 106, 24, 0, 1044, 228, 1, 0, 0, 0, 1045, 1050, 3, 65, 25, 0, 1046, 1050, 3, 63, 24, 0, 1047, 1050, 3, 79, 32, 0, 1048, 1050, 3, 153, 69, 0, 1049, 1045, 1, 0, 0, 0, 1049, 1046, 1, 0, 0, 0, 1049, 1047, 1, 0, 0, 0, 1049, 1048, 1, 0, 0, 0, 1050, 230, 1, 0, 0, 0, 1051, 1054, 3, 65, 25, 0, 1052, 1054, 3, 153, 69, 0, 1053, 1051, 1, 0, 0, 0, 1053, 1052, 1, 0, 0, 0, 1054, 1058, 1, 0, 0, 0, 1055, 1057, 3, 229, 107, 0, 1056, 1055, 1, 0, 0, 0, 1057, 1060, 1, 0, 0, 0, 1058, 1056, 1, 0, 0, 0, 1058, 1059, 1, 0, 0, 0, 1059, 1071, 1, 0, 0, 0, 1060, 1058, 1, 0, 0, 0, 1061, 1064, 3, 79, 32, 0, 1062, 1064, 3, 73, 29, 0, 1063, 1061, 1, 0, 0, 0, 1063, 1062, 1, 0, 0, 0, 1064, 1066, 1, 0, 0, 0, 1065, 1067, 3, 229, 107, 0, 1066, 1065, 1, 0, 0, 0, 1067, 1068, 1, 0, 0, 0, 1068, 1066, 1, 0, 0, 0, 1068, 1069, 1, 0, 0, 0, 1069, 1071, 1, 0, 0, 0, 1070, 1053, 1, 0, 0, 0, 1070, 1063, 1, 0, 0, 0, 1071, 232, 1, 0, 0, 0, 1072, 1075, 3, 231, 108, 0, 1073, 1075, 3, 171, 78, 0, 1074, 1072, 1, 0, 0, 0, 1074, 1073, 1, 0, 0, 0, 1075, 1076, 1, 0, 0, 0, 1076, 1074, 1, 0, 0, 0, 1076, 1077, 1, 0, 0, 0, 1077, 234, 1, 0, 0, 0, 1078, 1079, 3, 55, 20, 0, 1079, 1080, 1, 0, 0, 0, 1080, 1081, 6, 110, 10, 0, 1081, 236, 1, 0, 0, 0, 1082, 1083, 3, 57, 21, 0, 1083, 1084, 1, 0, 0, 0, 1084, 1085, 6, 111, 10, 0, 1085, 238, 1, 0, 0, 0, 1086, 1087, 3, 59, 22, 0, 1087, 1088, 1, 0, 0, 0, 1088, 1089, 6, 112, 10, 0, 1089, 240, 1, 0, 0, 0, 1090, 1091, 3, 61, 23, 0, 1091, 1092, 1, 0, 0, 0, 1092, 1093, 6, 113, 15, 0, 1093, 1094, 6, 113, 11, 0, 1094, 242, 1, 0, 0, 0, 1095, 1096, 3, 95, 40, 0, 1096, 1097, 1, 0, 0, 0, 1097, 1098, 6, 114, 19, 0, 1098, 244, 1, 0, 0, 0, 1099, 1100, 3, 99, 42, 0, 1100, 1101, 1, 0, 0, 0, 1101, 1102, 6, 115, 18, 0, 1102, 246, 1, 0, 0, 0, 1103, 1104, 3, 103, 44, 0, 1104, 1105, 1, 0, 0, 0, 1105, 1106, 6, 116, 22, 0, 1106, 248, 1, 0, 0, 0, 1107, 1108, 4, 117, 5, 0, 1108, 1109, 3, 127, 56, 0, 1109, 1110, 1, 0, 0, 0, 1110, 1111, 6, 117, 23, 0, 1111, 250, 1, 0, 0, 0, 1112, 1113, 4, 118, 6, 0, 1113, 1114, 3, 163, 74, 0, 1114, 1115, 1, 0, 0, 0, 1115, 1116, 6, 118, 24, 0, 1116, 252, 1, 0, 0, 0, 1117, 1118, 7, 12, 0, 0, 1118, 1119, 7, 2, 0, 0, 1119, 254, 1, 0, 0, 0, 1120, 1121, 3, 233, 109, 0, 1121, 1122, 1, 0, 0, 0, 1122, 1123, 6, 120, 25, 0, 1123, 256, 1, 0, 0, 0, 1124, 1125, 3, 55, 20, 0, 1125, 1126, 1, 0, 0, 0, 1126, 1127, 6, 121, 10, 0, 1127, 258, 1, 0, 0, 0, 1128, 1129, 3, 57, 21, 0, 1129, 1130, 1, 0, 0, 0, 1130, 1131, 6, 122, 10, 0, 1131, 260, 1, 0, 0, 0, 1132, 1133, 3, 59, 22, 0, 1133, 1134, 1, 0, 0, 0, 1134, 1135, 6, 123, 10, 0, 1135, 262, 1, 0, 0, 0, 1136, 1137, 3, 61, 23, 0, 1137, 1138, 1, 0, 0, 0, 1138, 1139, 6, 124, 15, 0, 1139, 1140, 6, 124, 11, 0, 1140, 264, 1, 0, 0, 0, 1141, 1142, 3, 165, 75, 0, 1142, 1143, 1, 0, 0, 0, 1143, 1144, 6, 125, 13, 0, 1144, 1145, 6, 125, 26, 0, 1145, 266, 1, 0, 0, 0, 1146, 1147, 7, 7, 0, 0, 1147, 1148, 7, 9, 0, 0, 1148, 1149, 1, 0, 0, 0, 1149, 1150, 6, 126, 27, 0, 1150, 268, 1, 0, 0, 0, 1151, 1152, 7, 19, 0, 0, 1152, 1153, 7, 1, 0, 0, 1153, 1154, 7, 5, 0, 0, 1154, 1155, 7, 10, 0, 0, 1155, 1156, 1, 0, 0, 0, 1156, 1157, 6, 127, 27, 0, 1157, 270, 1, 0, 0, 0, 1158, 1159, 8, 34, 0, 0, 1159, 272, 1, 0, 0, 0, 1160, 1162, 3, 271, 128, 0, 1161, 1160, 1, 0, 0, 0, 1162, 1163, 1, 0, 0, 0, 1163, 1161, 1, 0, 0, 0, 1163, 1164, 1, 0, 0, 0, 1164, 1165, 1, 0, 0, 0, 1165, 1166, 3, 337, 161, 0, 1166, 1168, 1, 0, 0, 0, 1167, 1161, 1, 0, 0, 0, 1167, 1168, 1, 0, 0, 0, 1168, 1170, 1, 0, 0, 0, 1169, 1171, 3, 271, 128, 0, 1170, 1169, 1, 0, 0, 0, 1171, 1172, 1, 0, 0, 0, 1172, 1170, 1, 0, 0, 0, 1172, 1173, 1, 0, 0, 0, 1173, 274, 1, 0, 0, 0, 1174, 1175, 3, 273, 129, 0, 1175, 1176, 1, 0, 0, 0, 1176, 1177, 6, 130, 28, 0, 1177, 276, 1, 0, 0, 0, 1178, 1179, 3, 55, 20, 0, 1179, 1180, 1, 0, 0, 0, 1180, 1181, 6, 131, 10, 0, 1181, 278, 1, 0, 0, 0, 1182, 1183, 3, 57, 21, 0, 1183, 1184, 1, 0, 0, 0, 1184, 1185, 6, 132, 10, 0, 1185, 280, 1, 0, 0, 0, 1186, 1187, 3, 59, 22, 0, 1187, 1188, 1, 0, 0, 0, 1188, 1189, 6, 133, 10, 0, 1189, 282, 1, 0, 0, 0, 1190, 1191, 3, 61, 23, 0, 1191, 1192, 1, 0, 0, 0, 1192, 1193, 6, 134, 15, 0, 1193, 1194, 6, 134, 11, 0, 1194, 1195, 6, 134, 11, 0, 1195, 284, 1, 0, 0, 0, 1196, 1197, 3, 95, 40, 0, 1197, 1198, 1, 0, 0, 0, 1198, 1199, 6, 135, 19, 0, 1199, 286, 1, 0, 0, 0, 1200, 1201, 3, 99, 42, 0, 1201, 1202, 1, 0, 0, 0, 1202, 1203, 6, 136, 18, 0, 1203, 288, 1, 0, 0, 0, 1204, 1205, 3, 103, 44, 0, 1205, 1206, 1, 0, 0, 0, 1206, 1207, 6, 137, 22, 0, 1207, 290, 1, 0, 0, 0, 1208, 1209, 3, 269, 127, 0, 1209, 1210, 1, 0, 0, 0, 1210, 1211, 6, 138, 29, 0, 1211, 292, 1, 0, 0, 0, 1212, 1213, 3, 233, 109, 0, 1213, 1214, 1, 0, 0, 0, 1214, 1215, 6, 139, 25, 0, 1215, 294, 1, 0, 0, 0, 1216, 1217, 3, 173, 79, 0, 1217, 1218, 1, 0, 0, 0, 1218, 1219, 6, 140, 30, 0, 1219, 296, 1, 0, 0, 0, 1220, 1221, 4, 141, 7, 0, 1221, 1222, 3, 127, 56, 0, 1222, 1223, 1, 0, 0, 0, 1223, 1224, 6, 141, 23, 0, 1224, 298, 1, 0, 0, 0, 1225, 1226, 4, 142, 8, 0, 1226, 1227, 3, 163, 74, 0, 1227, 1228, 1, 0, 0, 0, 1228, 1229, 6, 142, 24, 0, 1229, 300, 1, 0, 0, 0, 1230, 1231, 3, 55, 20, 0, 1231, 1232, 1, 0, 0, 0, 1232, 1233, 6, 143, 10, 0, 1233, 302, 1, 0, 0, 0, 1234, 1235, 3, 57, 21, 0, 1235, 1236, 1, 0, 0, 0, 1236, 1237, 6, 144, 10, 0, 1237, 304, 1, 0, 0, 0, 1238, 1239, 3, 59, 22, 0, 1239, 1240, 1, 0, 0, 0, 1240, 1241, 6, 145, 10, 0, 1241, 306, 1, 0, 0, 0, 1242, 1243, 3, 61, 23, 0, 1243, 1244, 1, 0, 0, 0, 1244, 1245, 6, 146, 15, 0, 1245, 1246, 6, 146, 11, 0, 1246, 308, 1, 0, 0, 0, 1247, 1248, 3, 103, 44, 0, 1248, 1249, 1, 0, 0, 0, 1249, 1250, 6, 147, 22, 0, 1250, 310, 1, 0, 0, 0, 1251, 1252, 4, 148, 9, 0, 1252, 1253, 3, 127, 56, 0, 1253, 1254, 1, 0, 0, 0, 1254, 1255, 6, 148, 23, 0, 1255, 312, 1, 0, 0, 0, 1256, 1257, 4, 149, 10, 0, 1257, 1258, 3, 163, 74, 0, 1258, 1259, 1, 0, 0, 0, 1259, 1260, 6, 149, 24, 0, 1260, 314, 1, 0, 0, 0, 1261, 1262, 3, 173, 79, 0, 1262, 1263, 1, 0, 0, 0, 1263, 1264, 6, 150, 30, 0, 1264, 316, 1, 0, 0, 0, 1265, 1266, 3, 169, 77, 0, 1266, 1267, 1, 0, 0, 0, 1267, 1268, 6, 151, 31, 0, 1268, 318, 1, 0, 0, 0, 1269, 1270, 3, 55, 20, 0, 1270, 1271, 1, 0, 0, 0, 1271, 1272, 6, 152, 10, 0, 1272, 320, 1, 0, 0, 0, 1273, 1274, 3, 57, 21, 0, 1274, 1275, 1, 0, 0, 0, 1275, 1276, 6, 153, 10, 0, 1276, 322, 1, 0, 0, 0, 1277, 1278, 3, 59, 22, 0, 1278, 1279, 1, 0, 0, 0, 1279, 1280, 6, 154, 10, 0, 1280, 324, 1, 0, 0, 0, 1281, 1282, 3, 61, 23, 0, 1282, 1283, 1, 0, 0, 0, 1283, 1284, 6, 155, 15, 0, 1284, 1285, 6, 155, 11, 0, 1285, 326, 1, 0, 0, 0, 1286, 1287, 7, 1, 0, 0, 1287, 1288, 7, 9, 0, 0, 1288, 1289, 7, 15, 0, 0, 1289, 1290, 7, 7, 0, 0, 1290, 328, 1, 0, 0, 0, 1291, 1292, 3, 55, 20, 0, 1292, 1293, 1, 0, 0, 0, 1293, 1294, 6, 157, 10, 0, 1294, 330, 1, 0, 0, 0, 1295, 1296, 3, 57, 21, 0, 1296, 1297, 1, 0, 0, 0, 1297, 1298, 6, 158, 10, 0, 1298, 332, 1, 0, 0, 0, 1299, 1300, 3, 59, 22, 0, 1300, 1301, 1, 0, 0, 0, 1301, 1302, 6, 159, 10, 0, 1302, 334, 1, 0, 0, 0, 1303, 1304, 3, 167, 76, 0, 1304, 1305, 1, 0, 0, 0, 1305, 1306, 6, 160, 16, 0, 1306, 1307, 6, 160, 11, 0, 1307, 336, 1, 0, 0, 0, 1308, 1309, 5, 58, 0, 0, 1309, 338, 1, 0, 0, 0, 1310, 1316, 3, 73, 29, 0, 1311, 1316, 3, 63, 24, 0, 1312, 1316, 3, 103, 44, 0, 1313, 1316, 3, 65, 25, 0, 1314, 1316, 3, 79, 32, 0, 1315, 1310, 1, 0, 0, 0, 1315, 1311, 1, 0, 0, 0, 1315, 1312, 1, 0, 0, 0, 1315, 1313, 1, 0, 0, 0, 1315, 1314, 1, 0, 0, 0, 1316, 1317, 1, 0, 0, 0, 1317, 1315, 1, 0, 0, 0, 1317, 1318, 1, 0, 0, 0, 1318, 340, 1, 0, 0, 0, 1319, 1320, 3, 55, 20, 0, 1320, 1321, 1, 0, 0, 0, 1321, 1322, 6, 163, 10, 0, 1322, 342, 1, 0, 0, 0, 1323, 1324, 3, 57, 21, 0, 1324, 1325, 1, 0, 0, 0, 1325, 1326, 6, 164, 10, 0, 1326, 344, 1, 0, 0, 0, 1327, 1328, 3, 59, 22, 0, 1328, 1329, 1, 0, 0, 0, 1329, 1330, 6, 165, 10, 0, 1330, 346, 1, 0, 0, 0, 1331, 1332, 3, 61, 23, 0, 1332, 1333, 1, 0, 0, 0, 1333, 1334, 6, 166, 15, 0, 1334, 1335, 6, 166, 11, 0, 1335, 348, 1, 0, 0, 0, 1336, 1337, 3, 337, 161, 0, 1337, 1338, 1, 0, 0, 0, 1338, 1339, 6, 167, 17, 0, 1339, 350, 1, 0, 0, 0, 1340, 1341, 3, 99, 42, 0, 1341, 1342, 1, 0, 0, 0, 1342, 1343, 6, 168, 18, 0, 1343, 352, 1, 0, 0, 0, 1344, 1345, 3, 103, 44, 0, 1345, 1346, 1, 0, 0, 0, 1346, 1347, 6, 169, 22, 0, 1347, 354, 1, 0, 0, 0, 1348, 1349, 3, 267, 126, 0, 1349, 1350, 1, 0, 0, 0, 1350, 1351, 6, 170, 32, 0, 1351, 1352, 6, 170, 33, 0, 1352, 356, 1, 0, 0, 0, 1353, 1354, 3, 207, 96, 0, 1354, 1355, 1, 0, 0, 0, 1355, 1356, 6, 171, 20, 0, 1356, 358, 1, 0, 0, 0, 1357, 1358, 3, 83, 34, 0, 1358, 1359, 1, 0, 0, 0, 1359, 1360, 6, 172, 21, 0, 1360, 360, 1, 0, 0, 0, 1361, 1362, 3, 55, 20, 0, 1362, 1363, 1, 0, 0, 0, 1363, 1364, 6, 173, 10, 0, 1364, 362, 1, 0, 0, 0, 1365, 1366, 3, 57, 21, 0, 1366, 1367, 1, 0, 0, 0, 1367, 1368, 6, 174, 10, 0, 1368, 364, 1, 0, 0, 0, 1369, 1370, 3, 59, 22, 0, 1370, 1371, 1, 0, 0, 0, 1371, 1372, 6, 175, 10, 0, 1372, 366, 1, 0, 0, 0, 1373, 1374, 3, 61, 23, 0, 1374, 1375, 1, 0, 0, 0, 1375, 1376, 6, 176, 15, 0, 1376, 1377, 6, 176, 11, 0, 1377, 1378, 6, 176, 11, 0, 1378, 368, 1, 0, 0, 0, 1379, 1380, 3, 99, 42, 0, 1380, 1381, 1, 0, 0, 0, 1381, 1382, 6, 177, 18, 0, 1382, 370, 1, 0, 0, 0, 1383, 1384, 3, 103, 44, 0, 1384, 1385, 1, 0, 0, 0, 1385, 1386, 6, 178, 22, 0, 1386, 372, 1, 0, 0, 0, 1387, 1388, 3, 233, 109, 0, 1388, 1389, 1, 0, 0, 0, 1389, 1390, 6, 179, 25, 0, 1390, 374, 1, 0, 0, 0, 1391, 1392, 3, 55, 20, 0, 1392, 1393, 1, 0, 0, 0, 1393, 1394, 6, 180, 10, 0, 1394, 376, 1, 0, 0, 0, 1395, 1396, 3, 57, 21, 0, 1396, 1397, 1, 0, 0, 0, 1397, 1398, 6, 181, 10, 0, 1398, 378, 1, 0, 0, 0, 1399, 1400, 3, 59, 22, 0, 1400, 1401, 1, 0, 0, 0, 1401, 1402, 6, 182, 10, 0, 1402, 380, 1, 0, 0, 0, 1403, 1404, 3, 61, 23, 0, 1404, 1405, 1, 0, 0, 0, 1405, 1406, 6, 183, 15, 0, 1406, 1407, 6, 183, 11, 0, 1407, 382, 1, 0, 0, 0, 1408, 1409, 3, 207, 96, 0, 1409, 1410, 1, 0, 0, 0, 1410, 1411, 6, 184, 20, 0, 1411, 1412, 6, 184, 11, 0, 1412, 1413, 6, 184, 34, 0, 1413, 384, 1, 0, 0, 0, 1414, 1415, 3, 83, 34, 0, 1415, 1416, 1, 0, 0, 0, 1416, 1417, 6, 185, 21, 0, 1417, 1418, 6, 185, 11, 0, 1418, 1419, 6, 185, 34, 0, 1419, 386, 1, 0, 0, 0, 1420, 1421, 3, 55, 20, 0, 1421, 1422, 1, 0, 0, 0, 1422, 1423, 6, 186, 10, 0, 1423, 388, 1, 0, 0, 0, 1424, 1425, 3, 57, 21, 0, 1425, 1426, 1, 0, 0, 0, 1426, 1427, 6, 187, 10, 0, 1427, 390, 1, 0, 0, 0, 1428, 1429, 3, 59, 22, 0, 1429, 1430, 1, 0, 0, 0, 1430, 1431, 6, 188, 10, 0, 1431, 392, 1, 0, 0, 0, 1432, 1433, 3, 337, 161, 0, 1433, 1434, 1, 0, 0, 0, 1434, 1435, 6, 189, 17, 0, 1435, 1436, 6, 189, 11, 0, 1436, 1437, 6, 189, 9, 0, 1437, 394, 1, 0, 0, 0, 1438, 1439, 3, 99, 42, 0, 1439, 1440, 1, 0, 0, 0, 1440, 1441, 6, 190, 18, 0, 1441, 1442, 6, 190, 11, 0, 1442, 1443, 6, 190, 9, 0, 1443, 396, 1, 0, 0, 0, 1444, 1445, 3, 55, 20, 0, 1445, 1446, 1, 0, 0, 0, 1446, 1447, 6, 191, 10, 0, 1447, 398, 1, 0, 0, 0, 1448, 1449, 3, 57, 21, 0, 1449, 1450, 1, 0, 0, 0, 1450, 1451, 6, 192, 10, 0, 1451, 400, 1, 0, 0, 0, 1452, 1453, 3, 59, 22, 0, 1453, 1454, 1, 0, 0, 0, 1454, 1455, 6, 193, 10, 0, 1455, 402, 1, 0, 0, 0, 1456, 1457, 3, 173, 79, 0, 1457, 1458, 1, 0, 0, 0, 1458, 1459, 6, 194, 11, 0, 1459, 1460, 6, 194, 0, 0, 1460, 1461, 6, 194, 30, 0, 1461, 404, 1, 0, 0, 0, 1462, 1463, 3, 169, 77, 0, 1463, 1464, 1, 0, 0, 0, 1464, 1465, 6, 195, 11, 0, 1465, 1466, 6, 195, 0, 0, 1466, 1467, 6, 195, 31, 0, 1467, 406, 1, 0, 0, 0, 1468, 1469, 3, 89, 37, 0, 1469, 1470, 1, 0, 0, 0, 1470, 1471, 6, 196, 11, 0, 1471, 1472, 6, 196, 0, 0, 1472, 1473, 6, 196, 35, 0, 1473, 408, 1, 0, 0, 0, 1474, 1475, 3, 61, 23, 0, 1475, 1476, 1, 0, 0, 0, 1476, 1477, 6, 197, 15, 0, 1477, 1478, 6, 197, 11, 0, 1478, 410, 1, 0, 0, 0, 65, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 579, 589, 593, 596, 605, 607, 618, 637, 642, 651, 658, 663, 665, 676, 684, 687, 689, 694, 699, 705, 712, 717, 723, 726, 734, 738, 870, 875, 882, 884, 900, 905, 910, 912, 918, 995, 1000, 1049, 1053, 1058, 1063, 1068, 1070, 1074, 1076, 1163, 1167, 1172, 1315, 1317, 36, 5, 1, 0, 5, 4, 0, 5, 6, 0, 5, 2, 0, 5, 3, 0, 5, 8, 0, 5, 5, 0, 5, 9, 0, 5, 11, 0, 5, 13, 0, 0, 1, 0, 4, 0, 0, 7, 16, 0, 7, 65, 0, 5, 0, 0, 7, 24, 0, 7, 66, 0, 7, 104, 0, 7, 33, 0, 7, 31, 0, 7, 76, 0, 7, 25, 0, 7, 35, 0, 7, 47, 0, 7, 64, 0, 7, 80, 0, 5, 10, 0, 5, 7, 0, 7, 90, 0, 7, 89, 0, 7, 68, 0, 7, 67, 0, 7, 88, 0, 5, 12, 0, 5, 14, 0, 7, 28, 0] \ No newline at end of file diff --git a/packages/kbn-esql-ast/src/antlr/esql_lexer.tokens b/packages/kbn-esql-ast/src/antlr/esql_lexer.tokens index 4fd37ab9900f2..4d1f426289149 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_lexer.tokens +++ b/packages/kbn-esql-ast/src/antlr/esql_lexer.tokens @@ -16,51 +16,51 @@ STATS=15 WHERE=16 DEV_INLINESTATS=17 DEV_LOOKUP=18 -DEV_MATCH=19 -DEV_METRICS=20 -UNKNOWN_CMD=21 -LINE_COMMENT=22 -MULTILINE_COMMENT=23 -WS=24 -PIPE=25 -QUOTED_STRING=26 -INTEGER_LITERAL=27 -DECIMAL_LITERAL=28 -BY=29 -AND=30 -ASC=31 -ASSIGN=32 -CAST_OP=33 -COMMA=34 -DESC=35 -DOT=36 -FALSE=37 -FIRST=38 -IN=39 -IS=40 -LAST=41 -LIKE=42 -LP=43 -NOT=44 -NULL=45 -NULLS=46 -OR=47 -PARAM=48 -RLIKE=49 -RP=50 -TRUE=51 -EQ=52 -CIEQ=53 -NEQ=54 -LT=55 -LTE=56 -GT=57 -GTE=58 -PLUS=59 -MINUS=60 -ASTERISK=61 -SLASH=62 -PERCENT=63 +DEV_METRICS=19 +UNKNOWN_CMD=20 +LINE_COMMENT=21 +MULTILINE_COMMENT=22 +WS=23 +PIPE=24 +QUOTED_STRING=25 +INTEGER_LITERAL=26 +DECIMAL_LITERAL=27 +BY=28 +AND=29 +ASC=30 +ASSIGN=31 +CAST_OP=32 +COMMA=33 +DESC=34 +DOT=35 +FALSE=36 +FIRST=37 +IN=38 +IS=39 +LAST=40 +LIKE=41 +LP=42 +NOT=43 +NULL=44 +NULLS=45 +OR=46 +PARAM=47 +RLIKE=48 +RP=49 +TRUE=50 +EQ=51 +CIEQ=52 +NEQ=53 +LT=54 +LTE=55 +GT=56 +GTE=57 +PLUS=58 +MINUS=59 +ASTERISK=60 +SLASH=61 +PERCENT=62 +MATCH=63 NAMED_OR_POSITIONAL_PARAM=64 OPENING_BRACKET=65 CLOSING_BRACKET=66 @@ -134,42 +134,43 @@ CLOSING_METRICS_WS=120 'sort'=14 'stats'=15 'where'=16 -'|'=25 -'by'=29 -'and'=30 -'asc'=31 -'='=32 -'::'=33 -','=34 -'desc'=35 -'.'=36 -'false'=37 -'first'=38 -'in'=39 -'is'=40 -'last'=41 -'like'=42 -'('=43 -'not'=44 -'null'=45 -'nulls'=46 -'or'=47 -'?'=48 -'rlike'=49 -')'=50 -'true'=51 -'=='=52 -'=~'=53 -'!='=54 -'<'=55 -'<='=56 -'>'=57 -'>='=58 -'+'=59 -'-'=60 -'*'=61 -'/'=62 -'%'=63 +'|'=24 +'by'=28 +'and'=29 +'asc'=30 +'='=31 +'::'=32 +','=33 +'desc'=34 +'.'=35 +'false'=36 +'first'=37 +'in'=38 +'is'=39 +'last'=40 +'like'=41 +'('=42 +'not'=43 +'null'=44 +'nulls'=45 +'or'=46 +'?'=47 +'rlike'=48 +')'=49 +'true'=50 +'=='=51 +'=~'=52 +'!='=53 +'<'=54 +'<='=55 +'>'=56 +'>='=57 +'+'=58 +'-'=59 +'*'=60 +'/'=61 +'%'=62 +'match'=63 ']'=66 'metadata'=75 'as'=84 diff --git a/packages/kbn-esql-ast/src/antlr/esql_lexer.ts b/packages/kbn-esql-ast/src/antlr/esql_lexer.ts index bbd8286b61d71..589148bf08c7c 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_lexer.ts +++ b/packages/kbn-esql-ast/src/antlr/esql_lexer.ts @@ -41,51 +41,51 @@ export default class esql_lexer extends lexer_config { public static readonly WHERE = 16; public static readonly DEV_INLINESTATS = 17; public static readonly DEV_LOOKUP = 18; - public static readonly DEV_MATCH = 19; - public static readonly DEV_METRICS = 20; - public static readonly UNKNOWN_CMD = 21; - public static readonly LINE_COMMENT = 22; - public static readonly MULTILINE_COMMENT = 23; - public static readonly WS = 24; - public static readonly PIPE = 25; - public static readonly QUOTED_STRING = 26; - public static readonly INTEGER_LITERAL = 27; - public static readonly DECIMAL_LITERAL = 28; - public static readonly BY = 29; - public static readonly AND = 30; - public static readonly ASC = 31; - public static readonly ASSIGN = 32; - public static readonly CAST_OP = 33; - public static readonly COMMA = 34; - public static readonly DESC = 35; - public static readonly DOT = 36; - public static readonly FALSE = 37; - public static readonly FIRST = 38; - public static readonly IN = 39; - public static readonly IS = 40; - public static readonly LAST = 41; - public static readonly LIKE = 42; - public static readonly LP = 43; - public static readonly NOT = 44; - public static readonly NULL = 45; - public static readonly NULLS = 46; - public static readonly OR = 47; - public static readonly PARAM = 48; - public static readonly RLIKE = 49; - public static readonly RP = 50; - public static readonly TRUE = 51; - public static readonly EQ = 52; - public static readonly CIEQ = 53; - public static readonly NEQ = 54; - public static readonly LT = 55; - public static readonly LTE = 56; - public static readonly GT = 57; - public static readonly GTE = 58; - public static readonly PLUS = 59; - public static readonly MINUS = 60; - public static readonly ASTERISK = 61; - public static readonly SLASH = 62; - public static readonly PERCENT = 63; + public static readonly DEV_METRICS = 19; + public static readonly UNKNOWN_CMD = 20; + public static readonly LINE_COMMENT = 21; + public static readonly MULTILINE_COMMENT = 22; + public static readonly WS = 23; + public static readonly PIPE = 24; + public static readonly QUOTED_STRING = 25; + public static readonly INTEGER_LITERAL = 26; + public static readonly DECIMAL_LITERAL = 27; + public static readonly BY = 28; + public static readonly AND = 29; + public static readonly ASC = 30; + public static readonly ASSIGN = 31; + public static readonly CAST_OP = 32; + public static readonly COMMA = 33; + public static readonly DESC = 34; + public static readonly DOT = 35; + public static readonly FALSE = 36; + public static readonly FIRST = 37; + public static readonly IN = 38; + public static readonly IS = 39; + public static readonly LAST = 40; + public static readonly LIKE = 41; + public static readonly LP = 42; + public static readonly NOT = 43; + public static readonly NULL = 44; + public static readonly NULLS = 45; + public static readonly OR = 46; + public static readonly PARAM = 47; + public static readonly RLIKE = 48; + public static readonly RP = 49; + public static readonly TRUE = 50; + public static readonly EQ = 51; + public static readonly CIEQ = 52; + public static readonly NEQ = 53; + public static readonly LT = 54; + public static readonly LTE = 55; + public static readonly GT = 56; + public static readonly GTE = 57; + public static readonly PLUS = 58; + public static readonly MINUS = 59; + public static readonly ASTERISK = 60; + public static readonly SLASH = 61; + public static readonly PERCENT = 62; + public static readonly MATCH = 63; public static readonly NAMED_OR_POSITIONAL_PARAM = 64; public static readonly OPENING_BRACKET = 65; public static readonly CLOSING_BRACKET = 66; @@ -173,26 +173,26 @@ export default class esql_lexer extends lexer_config { null, null, null, null, null, null, - null, "'|'", + "'|'", null, null, null, - null, "'by'", - "'and'", "'asc'", - "'='", "'::'", - "','", "'desc'", - "'.'", "'false'", - "'first'", "'in'", - "'is'", "'last'", - "'like'", "'('", - "'not'", "'null'", - "'nulls'", "'or'", - "'?'", "'rlike'", - "')'", "'true'", - "'=='", "'=~'", - "'!='", "'<'", - "'<='", "'>'", - "'>='", "'+'", - "'-'", "'*'", - "'/'", "'%'", + "'by'", "'and'", + "'asc'", "'='", + "'::'", "','", + "'desc'", "'.'", + "'false'", "'first'", + "'in'", "'is'", + "'last'", "'like'", + "'('", "'not'", + "'null'", "'nulls'", + "'or'", "'?'", + "'rlike'", "')'", + "'true'", "'=='", + "'=~'", "'!='", + "'<'", "'<='", + "'>'", "'>='", + "'+'", "'-'", + "'*'", "'/'", + "'%'", "'match'", null, null, "']'", null, null, null, @@ -225,7 +225,6 @@ export default class esql_lexer extends lexer_config { "STATS", "WHERE", "DEV_INLINESTATS", "DEV_LOOKUP", - "DEV_MATCH", "DEV_METRICS", "UNKNOWN_CMD", "LINE_COMMENT", @@ -252,7 +251,7 @@ export default class esql_lexer extends lexer_config { "GTE", "PLUS", "MINUS", "ASTERISK", "SLASH", "PERCENT", - "NAMED_OR_POSITIONAL_PARAM", + "MATCH", "NAMED_OR_POSITIONAL_PARAM", "OPENING_BRACKET", "CLOSING_BRACKET", "UNQUOTED_IDENTIFIER", @@ -317,20 +316,20 @@ export default class esql_lexer extends lexer_config { public static readonly ruleNames: string[] = [ "DISSECT", "DROP", "ENRICH", "EVAL", "EXPLAIN", "FROM", "GROK", "KEEP", "LIMIT", "MV_EXPAND", "RENAME", "ROW", "SHOW", "SORT", "STATS", "WHERE", - "DEV_INLINESTATS", "DEV_LOOKUP", "DEV_MATCH", "DEV_METRICS", "UNKNOWN_CMD", - "LINE_COMMENT", "MULTILINE_COMMENT", "WS", "PIPE", "DIGIT", "LETTER", - "ESCAPE_SEQUENCE", "UNESCAPED_CHARS", "EXPONENT", "ASPERAND", "BACKQUOTE", - "BACKQUOTE_BLOCK", "UNDERSCORE", "UNQUOTED_ID_BODY", "QUOTED_STRING", - "INTEGER_LITERAL", "DECIMAL_LITERAL", "BY", "AND", "ASC", "ASSIGN", "CAST_OP", - "COMMA", "DESC", "DOT", "FALSE", "FIRST", "IN", "IS", "LAST", "LIKE", - "LP", "NOT", "NULL", "NULLS", "OR", "PARAM", "RLIKE", "RP", "TRUE", "EQ", - "CIEQ", "NEQ", "LT", "LTE", "GT", "GTE", "PLUS", "MINUS", "ASTERISK", - "SLASH", "PERCENT", "DEV_MATCH_OP", "NAMED_OR_POSITIONAL_PARAM", "OPENING_BRACKET", - "CLOSING_BRACKET", "UNQUOTED_IDENTIFIER", "QUOTED_ID", "QUOTED_IDENTIFIER", - "EXPR_LINE_COMMENT", "EXPR_MULTILINE_COMMENT", "EXPR_WS", "EXPLAIN_OPENING_BRACKET", - "EXPLAIN_PIPE", "EXPLAIN_WS", "EXPLAIN_LINE_COMMENT", "EXPLAIN_MULTILINE_COMMENT", - "FROM_PIPE", "FROM_OPENING_BRACKET", "FROM_CLOSING_BRACKET", "FROM_COLON", - "FROM_COMMA", "FROM_ASSIGN", "METADATA", "UNQUOTED_SOURCE_PART", "UNQUOTED_SOURCE", + "DEV_INLINESTATS", "DEV_LOOKUP", "DEV_METRICS", "UNKNOWN_CMD", "LINE_COMMENT", + "MULTILINE_COMMENT", "WS", "PIPE", "DIGIT", "LETTER", "ESCAPE_SEQUENCE", + "UNESCAPED_CHARS", "EXPONENT", "ASPERAND", "BACKQUOTE", "BACKQUOTE_BLOCK", + "UNDERSCORE", "UNQUOTED_ID_BODY", "QUOTED_STRING", "INTEGER_LITERAL", + "DECIMAL_LITERAL", "BY", "AND", "ASC", "ASSIGN", "CAST_OP", "COMMA", "DESC", + "DOT", "FALSE", "FIRST", "IN", "IS", "LAST", "LIKE", "LP", "NOT", "NULL", + "NULLS", "OR", "PARAM", "RLIKE", "RP", "TRUE", "EQ", "CIEQ", "NEQ", "LT", + "LTE", "GT", "GTE", "PLUS", "MINUS", "ASTERISK", "SLASH", "PERCENT", "MATCH", + "NESTED_WHERE", "NAMED_OR_POSITIONAL_PARAM", "OPENING_BRACKET", "CLOSING_BRACKET", + "UNQUOTED_IDENTIFIER", "QUOTED_ID", "QUOTED_IDENTIFIER", "EXPR_LINE_COMMENT", + "EXPR_MULTILINE_COMMENT", "EXPR_WS", "EXPLAIN_OPENING_BRACKET", "EXPLAIN_PIPE", + "EXPLAIN_WS", "EXPLAIN_LINE_COMMENT", "EXPLAIN_MULTILINE_COMMENT", "FROM_PIPE", + "FROM_OPENING_BRACKET", "FROM_CLOSING_BRACKET", "FROM_COLON", "FROM_COMMA", + "FROM_ASSIGN", "METADATA", "UNQUOTED_SOURCE_PART", "UNQUOTED_SOURCE", "FROM_UNQUOTED_SOURCE", "FROM_QUOTED_SOURCE", "FROM_LINE_COMMENT", "FROM_MULTILINE_COMMENT", "FROM_WS", "PROJECT_PIPE", "PROJECT_DOT", "PROJECT_COMMA", "PROJECT_PARAM", "PROJECT_NAMED_OR_POSITIONAL_PARAM", "UNQUOTED_ID_BODY_WITH_PATTERN", @@ -386,11 +385,23 @@ export default class esql_lexer extends lexer_config { case 17: return this.DEV_LOOKUP_sempred(localctx, predIndex); case 18: - return this.DEV_MATCH_sempred(localctx, predIndex); - case 19: return this.DEV_METRICS_sempred(localctx, predIndex); - case 73: - return this.DEV_MATCH_OP_sempred(localctx, predIndex); + case 105: + return this.PROJECT_PARAM_sempred(localctx, predIndex); + case 106: + return this.PROJECT_NAMED_OR_POSITIONAL_PARAM_sempred(localctx, predIndex); + case 117: + return this.RENAME_PARAM_sempred(localctx, predIndex); + case 118: + return this.RENAME_NAMED_OR_POSITIONAL_PARAM_sempred(localctx, predIndex); + case 141: + return this.ENRICH_FIELD_PARAM_sempred(localctx, predIndex); + case 142: + return this.ENRICH_FIELD_NAMED_OR_POSITIONAL_PARAM_sempred(localctx, predIndex); + case 148: + return this.MVEXPAND_PARAM_sempred(localctx, predIndex); + case 149: + return this.MVEXPAND_NAMED_OR_POSITIONAL_PARAM_sempred(localctx, predIndex); } return true; } @@ -408,29 +419,71 @@ export default class esql_lexer extends lexer_config { } return true; } - private DEV_MATCH_sempred(localctx: RuleContext, predIndex: number): boolean { + private DEV_METRICS_sempred(localctx: RuleContext, predIndex: number): boolean { switch (predIndex) { case 2: return this.isDevVersion(); } return true; } - private DEV_METRICS_sempred(localctx: RuleContext, predIndex: number): boolean { + private PROJECT_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { switch (predIndex) { case 3: return this.isDevVersion(); } return true; } - private DEV_MATCH_OP_sempred(localctx: RuleContext, predIndex: number): boolean { + private PROJECT_NAMED_OR_POSITIONAL_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { switch (predIndex) { case 4: return this.isDevVersion(); } return true; } + private RENAME_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { + switch (predIndex) { + case 5: + return this.isDevVersion(); + } + return true; + } + private RENAME_NAMED_OR_POSITIONAL_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { + switch (predIndex) { + case 6: + return this.isDevVersion(); + } + return true; + } + private ENRICH_FIELD_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { + switch (predIndex) { + case 7: + return this.isDevVersion(); + } + return true; + } + private ENRICH_FIELD_NAMED_OR_POSITIONAL_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { + switch (predIndex) { + case 8: + return this.isDevVersion(); + } + return true; + } + private MVEXPAND_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { + switch (predIndex) { + case 9: + return this.isDevVersion(); + } + return true; + } + private MVEXPAND_NAMED_OR_POSITIONAL_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { + switch (predIndex) { + case 10: + return this.isDevVersion(); + } + return true; + } - public static readonly _serializedATN: number[] = [4,0,120,1475,6,-1,6, + public static readonly _serializedATN: number[] = [4,0,120,1479,6,-1,6, -1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,2,0, 7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,6,2,7,7,7,2,8,7,8,2,9, 7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,13,7,13,2,14,7,14,2,15,7,15,2,16,7, @@ -472,471 +525,472 @@ export default class esql_lexer extends lexer_config { 1,14,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,16,1,16,1,16,1,16,1,16,1, 16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,17,1,17,1,17,1,17,1,17, 1,17,1,17,1,17,1,17,1,17,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1, - 19,1,19,1,19,1,19,1,19,1,19,1,19,1,19,1,19,1,19,1,19,1,20,4,20,587,8,20, - 11,20,12,20,588,1,20,1,20,1,21,1,21,1,21,1,21,5,21,597,8,21,10,21,12,21, - 600,9,21,1,21,3,21,603,8,21,1,21,3,21,606,8,21,1,21,1,21,1,22,1,22,1,22, - 1,22,1,22,5,22,615,8,22,10,22,12,22,618,9,22,1,22,1,22,1,22,1,22,1,22,1, - 23,4,23,626,8,23,11,23,12,23,627,1,23,1,23,1,24,1,24,1,24,1,24,1,25,1,25, - 1,26,1,26,1,27,1,27,1,27,1,28,1,28,1,29,1,29,3,29,647,8,29,1,29,4,29,650, - 8,29,11,29,12,29,651,1,30,1,30,1,31,1,31,1,32,1,32,1,32,3,32,661,8,32,1, - 33,1,33,1,34,1,34,1,34,3,34,668,8,34,1,35,1,35,1,35,5,35,673,8,35,10,35, - 12,35,676,9,35,1,35,1,35,1,35,1,35,1,35,1,35,5,35,684,8,35,10,35,12,35, - 687,9,35,1,35,1,35,1,35,1,35,1,35,3,35,694,8,35,1,35,3,35,697,8,35,3,35, - 699,8,35,1,36,4,36,702,8,36,11,36,12,36,703,1,37,4,37,707,8,37,11,37,12, - 37,708,1,37,1,37,5,37,713,8,37,10,37,12,37,716,9,37,1,37,1,37,4,37,720, - 8,37,11,37,12,37,721,1,37,4,37,725,8,37,11,37,12,37,726,1,37,1,37,5,37, - 731,8,37,10,37,12,37,734,9,37,3,37,736,8,37,1,37,1,37,1,37,1,37,4,37,742, - 8,37,11,37,12,37,743,1,37,1,37,3,37,748,8,37,1,38,1,38,1,38,1,39,1,39,1, - 39,1,39,1,40,1,40,1,40,1,40,1,41,1,41,1,42,1,42,1,42,1,43,1,43,1,44,1,44, - 1,44,1,44,1,44,1,45,1,45,1,46,1,46,1,46,1,46,1,46,1,46,1,47,1,47,1,47,1, - 47,1,47,1,47,1,48,1,48,1,48,1,49,1,49,1,49,1,50,1,50,1,50,1,50,1,50,1,51, - 1,51,1,51,1,51,1,51,1,52,1,52,1,53,1,53,1,53,1,53,1,54,1,54,1,54,1,54,1, - 54,1,55,1,55,1,55,1,55,1,55,1,55,1,56,1,56,1,56,1,57,1,57,1,58,1,58,1,58, - 1,58,1,58,1,58,1,59,1,59,1,60,1,60,1,60,1,60,1,60,1,61,1,61,1,61,1,62,1, - 62,1,62,1,63,1,63,1,63,1,64,1,64,1,65,1,65,1,65,1,66,1,66,1,67,1,67,1,67, - 1,68,1,68,1,69,1,69,1,70,1,70,1,71,1,71,1,72,1,72,1,73,1,73,1,73,1,73,1, - 73,1,74,1,74,1,74,3,74,875,8,74,1,74,5,74,878,8,74,10,74,12,74,881,9,74, - 1,74,1,74,4,74,885,8,74,11,74,12,74,886,3,74,889,8,74,1,75,1,75,1,75,1, - 75,1,75,1,76,1,76,1,76,1,76,1,76,1,77,1,77,5,77,903,8,77,10,77,12,77,906, - 9,77,1,77,1,77,3,77,910,8,77,1,77,4,77,913,8,77,11,77,12,77,914,3,77,917, - 8,77,1,78,1,78,4,78,921,8,78,11,78,12,78,922,1,78,1,78,1,79,1,79,1,80,1, - 80,1,80,1,80,1,81,1,81,1,81,1,81,1,82,1,82,1,82,1,82,1,83,1,83,1,83,1,83, - 1,83,1,84,1,84,1,84,1,84,1,84,1,85,1,85,1,85,1,85,1,86,1,86,1,86,1,86,1, - 87,1,87,1,87,1,87,1,88,1,88,1,88,1,88,1,88,1,89,1,89,1,89,1,89,1,90,1,90, - 1,90,1,90,1,91,1,91,1,91,1,91,1,92,1,92,1,92,1,92,1,93,1,93,1,93,1,93,1, - 94,1,94,1,94,1,94,1,94,1,94,1,94,1,94,1,94,1,95,1,95,1,95,3,95,1000,8,95, - 1,96,4,96,1003,8,96,11,96,12,96,1004,1,97,1,97,1,97,1,97,1,98,1,98,1,98, - 1,98,1,99,1,99,1,99,1,99,1,100,1,100,1,100,1,100,1,101,1,101,1,101,1,101, - 1,102,1,102,1,102,1,102,1,102,1,103,1,103,1,103,1,103,1,104,1,104,1,104, - 1,104,1,105,1,105,1,105,1,105,1,106,1,106,1,106,1,106,1,107,1,107,1,107, - 1,107,3,107,1052,8,107,1,108,1,108,3,108,1056,8,108,1,108,5,108,1059,8, - 108,10,108,12,108,1062,9,108,1,108,1,108,3,108,1066,8,108,1,108,4,108,1069, - 8,108,11,108,12,108,1070,3,108,1073,8,108,1,109,1,109,4,109,1077,8,109, - 11,109,12,109,1078,1,110,1,110,1,110,1,110,1,111,1,111,1,111,1,111,1,112, - 1,112,1,112,1,112,1,113,1,113,1,113,1,113,1,113,1,114,1,114,1,114,1,114, - 1,115,1,115,1,115,1,115,1,116,1,116,1,116,1,116,1,117,1,117,1,117,1,117, - 1,118,1,118,1,118,1,118,1,119,1,119,1,119,1,120,1,120,1,120,1,120,1,121, - 1,121,1,121,1,121,1,122,1,122,1,122,1,122,1,123,1,123,1,123,1,123,1,124, - 1,124,1,124,1,124,1,124,1,125,1,125,1,125,1,125,1,125,1,126,1,126,1,126, - 1,126,1,126,1,127,1,127,1,127,1,127,1,127,1,127,1,127,1,128,1,128,1,129, - 4,129,1162,8,129,11,129,12,129,1163,1,129,1,129,3,129,1168,8,129,1,129, - 4,129,1171,8,129,11,129,12,129,1172,1,130,1,130,1,130,1,130,1,131,1,131, - 1,131,1,131,1,132,1,132,1,132,1,132,1,133,1,133,1,133,1,133,1,134,1,134, - 1,134,1,134,1,134,1,134,1,135,1,135,1,135,1,135,1,136,1,136,1,136,1,136, - 1,137,1,137,1,137,1,137,1,138,1,138,1,138,1,138,1,139,1,139,1,139,1,139, - 1,140,1,140,1,140,1,140,1,141,1,141,1,141,1,141,1,142,1,142,1,142,1,142, - 1,143,1,143,1,143,1,143,1,144,1,144,1,144,1,144,1,145,1,145,1,145,1,145, - 1,146,1,146,1,146,1,146,1,146,1,147,1,147,1,147,1,147,1,148,1,148,1,148, - 1,148,1,149,1,149,1,149,1,149,1,150,1,150,1,150,1,150,1,151,1,151,1,151, - 1,151,1,152,1,152,1,152,1,152,1,153,1,153,1,153,1,153,1,154,1,154,1,154, - 1,154,1,155,1,155,1,155,1,155,1,155,1,156,1,156,1,156,1,156,1,156,1,157, - 1,157,1,157,1,157,1,158,1,158,1,158,1,158,1,159,1,159,1,159,1,159,1,160, - 1,160,1,160,1,160,1,160,1,161,1,161,1,162,1,162,1,162,1,162,1,162,4,162, - 1312,8,162,11,162,12,162,1313,1,163,1,163,1,163,1,163,1,164,1,164,1,164, - 1,164,1,165,1,165,1,165,1,165,1,166,1,166,1,166,1,166,1,166,1,167,1,167, - 1,167,1,167,1,168,1,168,1,168,1,168,1,169,1,169,1,169,1,169,1,170,1,170, - 1,170,1,170,1,170,1,171,1,171,1,171,1,171,1,172,1,172,1,172,1,172,1,173, - 1,173,1,173,1,173,1,174,1,174,1,174,1,174,1,175,1,175,1,175,1,175,1,176, - 1,176,1,176,1,176,1,176,1,176,1,177,1,177,1,177,1,177,1,178,1,178,1,178, - 1,178,1,179,1,179,1,179,1,179,1,180,1,180,1,180,1,180,1,181,1,181,1,181, - 1,181,1,182,1,182,1,182,1,182,1,183,1,183,1,183,1,183,1,183,1,184,1,184, - 1,184,1,184,1,184,1,184,1,185,1,185,1,185,1,185,1,185,1,185,1,186,1,186, - 1,186,1,186,1,187,1,187,1,187,1,187,1,188,1,188,1,188,1,188,1,189,1,189, - 1,189,1,189,1,189,1,189,1,190,1,190,1,190,1,190,1,190,1,190,1,191,1,191, - 1,191,1,191,1,192,1,192,1,192,1,192,1,193,1,193,1,193,1,193,1,194,1,194, - 1,194,1,194,1,194,1,194,1,195,1,195,1,195,1,195,1,195,1,195,1,196,1,196, - 1,196,1,196,1,196,1,196,1,197,1,197,1,197,1,197,1,197,2,616,685,0,198,15, - 1,17,2,19,3,21,4,23,5,25,6,27,7,29,8,31,9,33,10,35,11,37,12,39,13,41,14, - 43,15,45,16,47,17,49,18,51,19,53,20,55,21,57,22,59,23,61,24,63,25,65,0, - 67,0,69,0,71,0,73,0,75,0,77,0,79,0,81,0,83,0,85,26,87,27,89,28,91,29,93, - 30,95,31,97,32,99,33,101,34,103,35,105,36,107,37,109,38,111,39,113,40,115, - 41,117,42,119,43,121,44,123,45,125,46,127,47,129,48,131,49,133,50,135,51, - 137,52,139,53,141,54,143,55,145,56,147,57,149,58,151,59,153,60,155,61,157, - 62,159,63,161,0,163,64,165,65,167,66,169,67,171,0,173,68,175,69,177,70, - 179,71,181,0,183,0,185,72,187,73,189,74,191,0,193,0,195,0,197,0,199,0,201, - 0,203,75,205,0,207,76,209,0,211,0,213,77,215,78,217,79,219,0,221,0,223, - 0,225,0,227,0,229,0,231,0,233,80,235,81,237,82,239,83,241,0,243,0,245,0, - 247,0,249,0,251,0,253,84,255,0,257,85,259,86,261,87,263,0,265,0,267,88, - 269,89,271,0,273,90,275,0,277,91,279,92,281,93,283,0,285,0,287,0,289,0, - 291,0,293,0,295,0,297,0,299,0,301,94,303,95,305,96,307,0,309,0,311,0,313, - 0,315,0,317,0,319,97,321,98,323,99,325,0,327,100,329,101,331,102,333,103, - 335,0,337,104,339,105,341,106,343,107,345,108,347,0,349,0,351,0,353,0,355, - 0,357,0,359,0,361,109,363,110,365,111,367,0,369,0,371,0,373,0,375,112,377, - 113,379,114,381,0,383,0,385,0,387,115,389,116,391,117,393,0,395,0,397,118, - 399,119,401,120,403,0,405,0,407,0,409,0,15,0,1,2,3,4,5,6,7,8,9,10,11,12, - 13,14,35,2,0,68,68,100,100,2,0,73,73,105,105,2,0,83,83,115,115,2,0,69,69, - 101,101,2,0,67,67,99,99,2,0,84,84,116,116,2,0,82,82,114,114,2,0,79,79,111, - 111,2,0,80,80,112,112,2,0,78,78,110,110,2,0,72,72,104,104,2,0,86,86,118, - 118,2,0,65,65,97,97,2,0,76,76,108,108,2,0,88,88,120,120,2,0,70,70,102,102, - 2,0,77,77,109,109,2,0,71,71,103,103,2,0,75,75,107,107,2,0,87,87,119,119, - 2,0,85,85,117,117,6,0,9,10,13,13,32,32,47,47,91,91,93,93,2,0,10,10,13,13, - 3,0,9,10,13,13,32,32,1,0,48,57,2,0,65,90,97,122,8,0,34,34,78,78,82,82,84, - 84,92,92,110,110,114,114,116,116,4,0,10,10,13,13,34,34,92,92,2,0,43,43, - 45,45,1,0,96,96,2,0,66,66,98,98,2,0,89,89,121,121,11,0,9,10,13,13,32,32, - 34,34,44,44,47,47,58,58,61,61,91,91,93,93,124,124,2,0,42,42,47,47,11,0, - 9,10,13,13,32,32,34,35,44,44,47,47,58,58,60,60,62,63,92,92,124,124,1503, - 0,15,1,0,0,0,0,17,1,0,0,0,0,19,1,0,0,0,0,21,1,0,0,0,0,23,1,0,0,0,0,25,1, - 0,0,0,0,27,1,0,0,0,0,29,1,0,0,0,0,31,1,0,0,0,0,33,1,0,0,0,0,35,1,0,0,0, - 0,37,1,0,0,0,0,39,1,0,0,0,0,41,1,0,0,0,0,43,1,0,0,0,0,45,1,0,0,0,0,47,1, - 0,0,0,0,49,1,0,0,0,0,51,1,0,0,0,0,53,1,0,0,0,0,55,1,0,0,0,0,57,1,0,0,0, - 0,59,1,0,0,0,0,61,1,0,0,0,1,63,1,0,0,0,1,85,1,0,0,0,1,87,1,0,0,0,1,89,1, - 0,0,0,1,91,1,0,0,0,1,93,1,0,0,0,1,95,1,0,0,0,1,97,1,0,0,0,1,99,1,0,0,0, - 1,101,1,0,0,0,1,103,1,0,0,0,1,105,1,0,0,0,1,107,1,0,0,0,1,109,1,0,0,0,1, - 111,1,0,0,0,1,113,1,0,0,0,1,115,1,0,0,0,1,117,1,0,0,0,1,119,1,0,0,0,1,121, - 1,0,0,0,1,123,1,0,0,0,1,125,1,0,0,0,1,127,1,0,0,0,1,129,1,0,0,0,1,131,1, - 0,0,0,1,133,1,0,0,0,1,135,1,0,0,0,1,137,1,0,0,0,1,139,1,0,0,0,1,141,1,0, - 0,0,1,143,1,0,0,0,1,145,1,0,0,0,1,147,1,0,0,0,1,149,1,0,0,0,1,151,1,0,0, - 0,1,153,1,0,0,0,1,155,1,0,0,0,1,157,1,0,0,0,1,159,1,0,0,0,1,161,1,0,0,0, - 1,163,1,0,0,0,1,165,1,0,0,0,1,167,1,0,0,0,1,169,1,0,0,0,1,173,1,0,0,0,1, - 175,1,0,0,0,1,177,1,0,0,0,1,179,1,0,0,0,2,181,1,0,0,0,2,183,1,0,0,0,2,185, - 1,0,0,0,2,187,1,0,0,0,2,189,1,0,0,0,3,191,1,0,0,0,3,193,1,0,0,0,3,195,1, - 0,0,0,3,197,1,0,0,0,3,199,1,0,0,0,3,201,1,0,0,0,3,203,1,0,0,0,3,207,1,0, - 0,0,3,209,1,0,0,0,3,211,1,0,0,0,3,213,1,0,0,0,3,215,1,0,0,0,3,217,1,0,0, - 0,4,219,1,0,0,0,4,221,1,0,0,0,4,223,1,0,0,0,4,225,1,0,0,0,4,227,1,0,0,0, - 4,233,1,0,0,0,4,235,1,0,0,0,4,237,1,0,0,0,4,239,1,0,0,0,5,241,1,0,0,0,5, - 243,1,0,0,0,5,245,1,0,0,0,5,247,1,0,0,0,5,249,1,0,0,0,5,251,1,0,0,0,5,253, - 1,0,0,0,5,255,1,0,0,0,5,257,1,0,0,0,5,259,1,0,0,0,5,261,1,0,0,0,6,263,1, - 0,0,0,6,265,1,0,0,0,6,267,1,0,0,0,6,269,1,0,0,0,6,273,1,0,0,0,6,275,1,0, - 0,0,6,277,1,0,0,0,6,279,1,0,0,0,6,281,1,0,0,0,7,283,1,0,0,0,7,285,1,0,0, - 0,7,287,1,0,0,0,7,289,1,0,0,0,7,291,1,0,0,0,7,293,1,0,0,0,7,295,1,0,0,0, - 7,297,1,0,0,0,7,299,1,0,0,0,7,301,1,0,0,0,7,303,1,0,0,0,7,305,1,0,0,0,8, - 307,1,0,0,0,8,309,1,0,0,0,8,311,1,0,0,0,8,313,1,0,0,0,8,315,1,0,0,0,8,317, - 1,0,0,0,8,319,1,0,0,0,8,321,1,0,0,0,8,323,1,0,0,0,9,325,1,0,0,0,9,327,1, - 0,0,0,9,329,1,0,0,0,9,331,1,0,0,0,9,333,1,0,0,0,10,335,1,0,0,0,10,337,1, - 0,0,0,10,339,1,0,0,0,10,341,1,0,0,0,10,343,1,0,0,0,10,345,1,0,0,0,11,347, - 1,0,0,0,11,349,1,0,0,0,11,351,1,0,0,0,11,353,1,0,0,0,11,355,1,0,0,0,11, - 357,1,0,0,0,11,359,1,0,0,0,11,361,1,0,0,0,11,363,1,0,0,0,11,365,1,0,0,0, - 12,367,1,0,0,0,12,369,1,0,0,0,12,371,1,0,0,0,12,373,1,0,0,0,12,375,1,0, - 0,0,12,377,1,0,0,0,12,379,1,0,0,0,13,381,1,0,0,0,13,383,1,0,0,0,13,385, - 1,0,0,0,13,387,1,0,0,0,13,389,1,0,0,0,13,391,1,0,0,0,14,393,1,0,0,0,14, - 395,1,0,0,0,14,397,1,0,0,0,14,399,1,0,0,0,14,401,1,0,0,0,14,403,1,0,0,0, - 14,405,1,0,0,0,14,407,1,0,0,0,14,409,1,0,0,0,15,411,1,0,0,0,17,421,1,0, - 0,0,19,428,1,0,0,0,21,437,1,0,0,0,23,444,1,0,0,0,25,454,1,0,0,0,27,461, - 1,0,0,0,29,468,1,0,0,0,31,475,1,0,0,0,33,483,1,0,0,0,35,495,1,0,0,0,37, - 504,1,0,0,0,39,510,1,0,0,0,41,517,1,0,0,0,43,524,1,0,0,0,45,532,1,0,0,0, - 47,540,1,0,0,0,49,555,1,0,0,0,51,565,1,0,0,0,53,574,1,0,0,0,55,586,1,0, - 0,0,57,592,1,0,0,0,59,609,1,0,0,0,61,625,1,0,0,0,63,631,1,0,0,0,65,635, - 1,0,0,0,67,637,1,0,0,0,69,639,1,0,0,0,71,642,1,0,0,0,73,644,1,0,0,0,75, - 653,1,0,0,0,77,655,1,0,0,0,79,660,1,0,0,0,81,662,1,0,0,0,83,667,1,0,0,0, - 85,698,1,0,0,0,87,701,1,0,0,0,89,747,1,0,0,0,91,749,1,0,0,0,93,752,1,0, - 0,0,95,756,1,0,0,0,97,760,1,0,0,0,99,762,1,0,0,0,101,765,1,0,0,0,103,767, - 1,0,0,0,105,772,1,0,0,0,107,774,1,0,0,0,109,780,1,0,0,0,111,786,1,0,0,0, - 113,789,1,0,0,0,115,792,1,0,0,0,117,797,1,0,0,0,119,802,1,0,0,0,121,804, - 1,0,0,0,123,808,1,0,0,0,125,813,1,0,0,0,127,819,1,0,0,0,129,822,1,0,0,0, - 131,824,1,0,0,0,133,830,1,0,0,0,135,832,1,0,0,0,137,837,1,0,0,0,139,840, - 1,0,0,0,141,843,1,0,0,0,143,846,1,0,0,0,145,848,1,0,0,0,147,851,1,0,0,0, - 149,853,1,0,0,0,151,856,1,0,0,0,153,858,1,0,0,0,155,860,1,0,0,0,157,862, - 1,0,0,0,159,864,1,0,0,0,161,866,1,0,0,0,163,888,1,0,0,0,165,890,1,0,0,0, - 167,895,1,0,0,0,169,916,1,0,0,0,171,918,1,0,0,0,173,926,1,0,0,0,175,928, - 1,0,0,0,177,932,1,0,0,0,179,936,1,0,0,0,181,940,1,0,0,0,183,945,1,0,0,0, - 185,950,1,0,0,0,187,954,1,0,0,0,189,958,1,0,0,0,191,962,1,0,0,0,193,967, - 1,0,0,0,195,971,1,0,0,0,197,975,1,0,0,0,199,979,1,0,0,0,201,983,1,0,0,0, - 203,987,1,0,0,0,205,999,1,0,0,0,207,1002,1,0,0,0,209,1006,1,0,0,0,211,1010, - 1,0,0,0,213,1014,1,0,0,0,215,1018,1,0,0,0,217,1022,1,0,0,0,219,1026,1,0, - 0,0,221,1031,1,0,0,0,223,1035,1,0,0,0,225,1039,1,0,0,0,227,1043,1,0,0,0, - 229,1051,1,0,0,0,231,1072,1,0,0,0,233,1076,1,0,0,0,235,1080,1,0,0,0,237, - 1084,1,0,0,0,239,1088,1,0,0,0,241,1092,1,0,0,0,243,1097,1,0,0,0,245,1101, - 1,0,0,0,247,1105,1,0,0,0,249,1109,1,0,0,0,251,1113,1,0,0,0,253,1117,1,0, - 0,0,255,1120,1,0,0,0,257,1124,1,0,0,0,259,1128,1,0,0,0,261,1132,1,0,0,0, - 263,1136,1,0,0,0,265,1141,1,0,0,0,267,1146,1,0,0,0,269,1151,1,0,0,0,271, - 1158,1,0,0,0,273,1167,1,0,0,0,275,1174,1,0,0,0,277,1178,1,0,0,0,279,1182, - 1,0,0,0,281,1186,1,0,0,0,283,1190,1,0,0,0,285,1196,1,0,0,0,287,1200,1,0, - 0,0,289,1204,1,0,0,0,291,1208,1,0,0,0,293,1212,1,0,0,0,295,1216,1,0,0,0, - 297,1220,1,0,0,0,299,1224,1,0,0,0,301,1228,1,0,0,0,303,1232,1,0,0,0,305, - 1236,1,0,0,0,307,1240,1,0,0,0,309,1245,1,0,0,0,311,1249,1,0,0,0,313,1253, - 1,0,0,0,315,1257,1,0,0,0,317,1261,1,0,0,0,319,1265,1,0,0,0,321,1269,1,0, - 0,0,323,1273,1,0,0,0,325,1277,1,0,0,0,327,1282,1,0,0,0,329,1287,1,0,0,0, - 331,1291,1,0,0,0,333,1295,1,0,0,0,335,1299,1,0,0,0,337,1304,1,0,0,0,339, - 1311,1,0,0,0,341,1315,1,0,0,0,343,1319,1,0,0,0,345,1323,1,0,0,0,347,1327, - 1,0,0,0,349,1332,1,0,0,0,351,1336,1,0,0,0,353,1340,1,0,0,0,355,1344,1,0, - 0,0,357,1349,1,0,0,0,359,1353,1,0,0,0,361,1357,1,0,0,0,363,1361,1,0,0,0, - 365,1365,1,0,0,0,367,1369,1,0,0,0,369,1375,1,0,0,0,371,1379,1,0,0,0,373, - 1383,1,0,0,0,375,1387,1,0,0,0,377,1391,1,0,0,0,379,1395,1,0,0,0,381,1399, - 1,0,0,0,383,1404,1,0,0,0,385,1410,1,0,0,0,387,1416,1,0,0,0,389,1420,1,0, - 0,0,391,1424,1,0,0,0,393,1428,1,0,0,0,395,1434,1,0,0,0,397,1440,1,0,0,0, - 399,1444,1,0,0,0,401,1448,1,0,0,0,403,1452,1,0,0,0,405,1458,1,0,0,0,407, - 1464,1,0,0,0,409,1470,1,0,0,0,411,412,7,0,0,0,412,413,7,1,0,0,413,414,7, - 2,0,0,414,415,7,2,0,0,415,416,7,3,0,0,416,417,7,4,0,0,417,418,7,5,0,0,418, - 419,1,0,0,0,419,420,6,0,0,0,420,16,1,0,0,0,421,422,7,0,0,0,422,423,7,6, - 0,0,423,424,7,7,0,0,424,425,7,8,0,0,425,426,1,0,0,0,426,427,6,1,1,0,427, - 18,1,0,0,0,428,429,7,3,0,0,429,430,7,9,0,0,430,431,7,6,0,0,431,432,7,1, - 0,0,432,433,7,4,0,0,433,434,7,10,0,0,434,435,1,0,0,0,435,436,6,2,2,0,436, - 20,1,0,0,0,437,438,7,3,0,0,438,439,7,11,0,0,439,440,7,12,0,0,440,441,7, - 13,0,0,441,442,1,0,0,0,442,443,6,3,0,0,443,22,1,0,0,0,444,445,7,3,0,0,445, - 446,7,14,0,0,446,447,7,8,0,0,447,448,7,13,0,0,448,449,7,12,0,0,449,450, - 7,1,0,0,450,451,7,9,0,0,451,452,1,0,0,0,452,453,6,4,3,0,453,24,1,0,0,0, - 454,455,7,15,0,0,455,456,7,6,0,0,456,457,7,7,0,0,457,458,7,16,0,0,458,459, - 1,0,0,0,459,460,6,5,4,0,460,26,1,0,0,0,461,462,7,17,0,0,462,463,7,6,0,0, - 463,464,7,7,0,0,464,465,7,18,0,0,465,466,1,0,0,0,466,467,6,6,0,0,467,28, - 1,0,0,0,468,469,7,18,0,0,469,470,7,3,0,0,470,471,7,3,0,0,471,472,7,8,0, - 0,472,473,1,0,0,0,473,474,6,7,1,0,474,30,1,0,0,0,475,476,7,13,0,0,476,477, - 7,1,0,0,477,478,7,16,0,0,478,479,7,1,0,0,479,480,7,5,0,0,480,481,1,0,0, - 0,481,482,6,8,0,0,482,32,1,0,0,0,483,484,7,16,0,0,484,485,7,11,0,0,485, - 486,5,95,0,0,486,487,7,3,0,0,487,488,7,14,0,0,488,489,7,8,0,0,489,490,7, - 12,0,0,490,491,7,9,0,0,491,492,7,0,0,0,492,493,1,0,0,0,493,494,6,9,5,0, - 494,34,1,0,0,0,495,496,7,6,0,0,496,497,7,3,0,0,497,498,7,9,0,0,498,499, - 7,12,0,0,499,500,7,16,0,0,500,501,7,3,0,0,501,502,1,0,0,0,502,503,6,10, - 6,0,503,36,1,0,0,0,504,505,7,6,0,0,505,506,7,7,0,0,506,507,7,19,0,0,507, - 508,1,0,0,0,508,509,6,11,0,0,509,38,1,0,0,0,510,511,7,2,0,0,511,512,7,10, - 0,0,512,513,7,7,0,0,513,514,7,19,0,0,514,515,1,0,0,0,515,516,6,12,7,0,516, - 40,1,0,0,0,517,518,7,2,0,0,518,519,7,7,0,0,519,520,7,6,0,0,520,521,7,5, - 0,0,521,522,1,0,0,0,522,523,6,13,0,0,523,42,1,0,0,0,524,525,7,2,0,0,525, - 526,7,5,0,0,526,527,7,12,0,0,527,528,7,5,0,0,528,529,7,2,0,0,529,530,1, - 0,0,0,530,531,6,14,0,0,531,44,1,0,0,0,532,533,7,19,0,0,533,534,7,10,0,0, - 534,535,7,3,0,0,535,536,7,6,0,0,536,537,7,3,0,0,537,538,1,0,0,0,538,539, - 6,15,0,0,539,46,1,0,0,0,540,541,4,16,0,0,541,542,7,1,0,0,542,543,7,9,0, - 0,543,544,7,13,0,0,544,545,7,1,0,0,545,546,7,9,0,0,546,547,7,3,0,0,547, - 548,7,2,0,0,548,549,7,5,0,0,549,550,7,12,0,0,550,551,7,5,0,0,551,552,7, - 2,0,0,552,553,1,0,0,0,553,554,6,16,0,0,554,48,1,0,0,0,555,556,4,17,1,0, - 556,557,7,13,0,0,557,558,7,7,0,0,558,559,7,7,0,0,559,560,7,18,0,0,560,561, - 7,20,0,0,561,562,7,8,0,0,562,563,1,0,0,0,563,564,6,17,8,0,564,50,1,0,0, - 0,565,566,4,18,2,0,566,567,7,16,0,0,567,568,7,12,0,0,568,569,7,5,0,0,569, - 570,7,4,0,0,570,571,7,10,0,0,571,572,1,0,0,0,572,573,6,18,0,0,573,52,1, - 0,0,0,574,575,4,19,3,0,575,576,7,16,0,0,576,577,7,3,0,0,577,578,7,5,0,0, - 578,579,7,6,0,0,579,580,7,1,0,0,580,581,7,4,0,0,581,582,7,2,0,0,582,583, - 1,0,0,0,583,584,6,19,9,0,584,54,1,0,0,0,585,587,8,21,0,0,586,585,1,0,0, - 0,587,588,1,0,0,0,588,586,1,0,0,0,588,589,1,0,0,0,589,590,1,0,0,0,590,591, - 6,20,0,0,591,56,1,0,0,0,592,593,5,47,0,0,593,594,5,47,0,0,594,598,1,0,0, - 0,595,597,8,22,0,0,596,595,1,0,0,0,597,600,1,0,0,0,598,596,1,0,0,0,598, - 599,1,0,0,0,599,602,1,0,0,0,600,598,1,0,0,0,601,603,5,13,0,0,602,601,1, - 0,0,0,602,603,1,0,0,0,603,605,1,0,0,0,604,606,5,10,0,0,605,604,1,0,0,0, - 605,606,1,0,0,0,606,607,1,0,0,0,607,608,6,21,10,0,608,58,1,0,0,0,609,610, - 5,47,0,0,610,611,5,42,0,0,611,616,1,0,0,0,612,615,3,59,22,0,613,615,9,0, - 0,0,614,612,1,0,0,0,614,613,1,0,0,0,615,618,1,0,0,0,616,617,1,0,0,0,616, - 614,1,0,0,0,617,619,1,0,0,0,618,616,1,0,0,0,619,620,5,42,0,0,620,621,5, - 47,0,0,621,622,1,0,0,0,622,623,6,22,10,0,623,60,1,0,0,0,624,626,7,23,0, - 0,625,624,1,0,0,0,626,627,1,0,0,0,627,625,1,0,0,0,627,628,1,0,0,0,628,629, - 1,0,0,0,629,630,6,23,10,0,630,62,1,0,0,0,631,632,5,124,0,0,632,633,1,0, - 0,0,633,634,6,24,11,0,634,64,1,0,0,0,635,636,7,24,0,0,636,66,1,0,0,0,637, - 638,7,25,0,0,638,68,1,0,0,0,639,640,5,92,0,0,640,641,7,26,0,0,641,70,1, - 0,0,0,642,643,8,27,0,0,643,72,1,0,0,0,644,646,7,3,0,0,645,647,7,28,0,0, - 646,645,1,0,0,0,646,647,1,0,0,0,647,649,1,0,0,0,648,650,3,65,25,0,649,648, - 1,0,0,0,650,651,1,0,0,0,651,649,1,0,0,0,651,652,1,0,0,0,652,74,1,0,0,0, - 653,654,5,64,0,0,654,76,1,0,0,0,655,656,5,96,0,0,656,78,1,0,0,0,657,661, - 8,29,0,0,658,659,5,96,0,0,659,661,5,96,0,0,660,657,1,0,0,0,660,658,1,0, - 0,0,661,80,1,0,0,0,662,663,5,95,0,0,663,82,1,0,0,0,664,668,3,67,26,0,665, - 668,3,65,25,0,666,668,3,81,33,0,667,664,1,0,0,0,667,665,1,0,0,0,667,666, - 1,0,0,0,668,84,1,0,0,0,669,674,5,34,0,0,670,673,3,69,27,0,671,673,3,71, - 28,0,672,670,1,0,0,0,672,671,1,0,0,0,673,676,1,0,0,0,674,672,1,0,0,0,674, - 675,1,0,0,0,675,677,1,0,0,0,676,674,1,0,0,0,677,699,5,34,0,0,678,679,5, - 34,0,0,679,680,5,34,0,0,680,681,5,34,0,0,681,685,1,0,0,0,682,684,8,22,0, - 0,683,682,1,0,0,0,684,687,1,0,0,0,685,686,1,0,0,0,685,683,1,0,0,0,686,688, - 1,0,0,0,687,685,1,0,0,0,688,689,5,34,0,0,689,690,5,34,0,0,690,691,5,34, - 0,0,691,693,1,0,0,0,692,694,5,34,0,0,693,692,1,0,0,0,693,694,1,0,0,0,694, - 696,1,0,0,0,695,697,5,34,0,0,696,695,1,0,0,0,696,697,1,0,0,0,697,699,1, - 0,0,0,698,669,1,0,0,0,698,678,1,0,0,0,699,86,1,0,0,0,700,702,3,65,25,0, - 701,700,1,0,0,0,702,703,1,0,0,0,703,701,1,0,0,0,703,704,1,0,0,0,704,88, - 1,0,0,0,705,707,3,65,25,0,706,705,1,0,0,0,707,708,1,0,0,0,708,706,1,0,0, - 0,708,709,1,0,0,0,709,710,1,0,0,0,710,714,3,105,45,0,711,713,3,65,25,0, - 712,711,1,0,0,0,713,716,1,0,0,0,714,712,1,0,0,0,714,715,1,0,0,0,715,748, - 1,0,0,0,716,714,1,0,0,0,717,719,3,105,45,0,718,720,3,65,25,0,719,718,1, - 0,0,0,720,721,1,0,0,0,721,719,1,0,0,0,721,722,1,0,0,0,722,748,1,0,0,0,723, - 725,3,65,25,0,724,723,1,0,0,0,725,726,1,0,0,0,726,724,1,0,0,0,726,727,1, - 0,0,0,727,735,1,0,0,0,728,732,3,105,45,0,729,731,3,65,25,0,730,729,1,0, - 0,0,731,734,1,0,0,0,732,730,1,0,0,0,732,733,1,0,0,0,733,736,1,0,0,0,734, - 732,1,0,0,0,735,728,1,0,0,0,735,736,1,0,0,0,736,737,1,0,0,0,737,738,3,73, - 29,0,738,748,1,0,0,0,739,741,3,105,45,0,740,742,3,65,25,0,741,740,1,0,0, - 0,742,743,1,0,0,0,743,741,1,0,0,0,743,744,1,0,0,0,744,745,1,0,0,0,745,746, - 3,73,29,0,746,748,1,0,0,0,747,706,1,0,0,0,747,717,1,0,0,0,747,724,1,0,0, - 0,747,739,1,0,0,0,748,90,1,0,0,0,749,750,7,30,0,0,750,751,7,31,0,0,751, - 92,1,0,0,0,752,753,7,12,0,0,753,754,7,9,0,0,754,755,7,0,0,0,755,94,1,0, - 0,0,756,757,7,12,0,0,757,758,7,2,0,0,758,759,7,4,0,0,759,96,1,0,0,0,760, - 761,5,61,0,0,761,98,1,0,0,0,762,763,5,58,0,0,763,764,5,58,0,0,764,100,1, - 0,0,0,765,766,5,44,0,0,766,102,1,0,0,0,767,768,7,0,0,0,768,769,7,3,0,0, - 769,770,7,2,0,0,770,771,7,4,0,0,771,104,1,0,0,0,772,773,5,46,0,0,773,106, - 1,0,0,0,774,775,7,15,0,0,775,776,7,12,0,0,776,777,7,13,0,0,777,778,7,2, - 0,0,778,779,7,3,0,0,779,108,1,0,0,0,780,781,7,15,0,0,781,782,7,1,0,0,782, - 783,7,6,0,0,783,784,7,2,0,0,784,785,7,5,0,0,785,110,1,0,0,0,786,787,7,1, - 0,0,787,788,7,9,0,0,788,112,1,0,0,0,789,790,7,1,0,0,790,791,7,2,0,0,791, - 114,1,0,0,0,792,793,7,13,0,0,793,794,7,12,0,0,794,795,7,2,0,0,795,796,7, - 5,0,0,796,116,1,0,0,0,797,798,7,13,0,0,798,799,7,1,0,0,799,800,7,18,0,0, - 800,801,7,3,0,0,801,118,1,0,0,0,802,803,5,40,0,0,803,120,1,0,0,0,804,805, - 7,9,0,0,805,806,7,7,0,0,806,807,7,5,0,0,807,122,1,0,0,0,808,809,7,9,0,0, - 809,810,7,20,0,0,810,811,7,13,0,0,811,812,7,13,0,0,812,124,1,0,0,0,813, - 814,7,9,0,0,814,815,7,20,0,0,815,816,7,13,0,0,816,817,7,13,0,0,817,818, - 7,2,0,0,818,126,1,0,0,0,819,820,7,7,0,0,820,821,7,6,0,0,821,128,1,0,0,0, - 822,823,5,63,0,0,823,130,1,0,0,0,824,825,7,6,0,0,825,826,7,13,0,0,826,827, - 7,1,0,0,827,828,7,18,0,0,828,829,7,3,0,0,829,132,1,0,0,0,830,831,5,41,0, - 0,831,134,1,0,0,0,832,833,7,5,0,0,833,834,7,6,0,0,834,835,7,20,0,0,835, - 836,7,3,0,0,836,136,1,0,0,0,837,838,5,61,0,0,838,839,5,61,0,0,839,138,1, - 0,0,0,840,841,5,61,0,0,841,842,5,126,0,0,842,140,1,0,0,0,843,844,5,33,0, - 0,844,845,5,61,0,0,845,142,1,0,0,0,846,847,5,60,0,0,847,144,1,0,0,0,848, - 849,5,60,0,0,849,850,5,61,0,0,850,146,1,0,0,0,851,852,5,62,0,0,852,148, - 1,0,0,0,853,854,5,62,0,0,854,855,5,61,0,0,855,150,1,0,0,0,856,857,5,43, - 0,0,857,152,1,0,0,0,858,859,5,45,0,0,859,154,1,0,0,0,860,861,5,42,0,0,861, - 156,1,0,0,0,862,863,5,47,0,0,863,158,1,0,0,0,864,865,5,37,0,0,865,160,1, - 0,0,0,866,867,4,73,4,0,867,868,3,51,18,0,868,869,1,0,0,0,869,870,6,73,12, - 0,870,162,1,0,0,0,871,874,3,129,57,0,872,875,3,67,26,0,873,875,3,81,33, - 0,874,872,1,0,0,0,874,873,1,0,0,0,875,879,1,0,0,0,876,878,3,83,34,0,877, - 876,1,0,0,0,878,881,1,0,0,0,879,877,1,0,0,0,879,880,1,0,0,0,880,889,1,0, - 0,0,881,879,1,0,0,0,882,884,3,129,57,0,883,885,3,65,25,0,884,883,1,0,0, - 0,885,886,1,0,0,0,886,884,1,0,0,0,886,887,1,0,0,0,887,889,1,0,0,0,888,871, - 1,0,0,0,888,882,1,0,0,0,889,164,1,0,0,0,890,891,5,91,0,0,891,892,1,0,0, - 0,892,893,6,75,0,0,893,894,6,75,0,0,894,166,1,0,0,0,895,896,5,93,0,0,896, - 897,1,0,0,0,897,898,6,76,11,0,898,899,6,76,11,0,899,168,1,0,0,0,900,904, - 3,67,26,0,901,903,3,83,34,0,902,901,1,0,0,0,903,906,1,0,0,0,904,902,1,0, - 0,0,904,905,1,0,0,0,905,917,1,0,0,0,906,904,1,0,0,0,907,910,3,81,33,0,908, - 910,3,75,30,0,909,907,1,0,0,0,909,908,1,0,0,0,910,912,1,0,0,0,911,913,3, - 83,34,0,912,911,1,0,0,0,913,914,1,0,0,0,914,912,1,0,0,0,914,915,1,0,0,0, - 915,917,1,0,0,0,916,900,1,0,0,0,916,909,1,0,0,0,917,170,1,0,0,0,918,920, - 3,77,31,0,919,921,3,79,32,0,920,919,1,0,0,0,921,922,1,0,0,0,922,920,1,0, - 0,0,922,923,1,0,0,0,923,924,1,0,0,0,924,925,3,77,31,0,925,172,1,0,0,0,926, - 927,3,171,78,0,927,174,1,0,0,0,928,929,3,57,21,0,929,930,1,0,0,0,930,931, - 6,80,10,0,931,176,1,0,0,0,932,933,3,59,22,0,933,934,1,0,0,0,934,935,6,81, - 10,0,935,178,1,0,0,0,936,937,3,61,23,0,937,938,1,0,0,0,938,939,6,82,10, - 0,939,180,1,0,0,0,940,941,3,165,75,0,941,942,1,0,0,0,942,943,6,83,13,0, - 943,944,6,83,14,0,944,182,1,0,0,0,945,946,3,63,24,0,946,947,1,0,0,0,947, - 948,6,84,15,0,948,949,6,84,11,0,949,184,1,0,0,0,950,951,3,61,23,0,951,952, - 1,0,0,0,952,953,6,85,10,0,953,186,1,0,0,0,954,955,3,57,21,0,955,956,1,0, - 0,0,956,957,6,86,10,0,957,188,1,0,0,0,958,959,3,59,22,0,959,960,1,0,0,0, - 960,961,6,87,10,0,961,190,1,0,0,0,962,963,3,63,24,0,963,964,1,0,0,0,964, - 965,6,88,15,0,965,966,6,88,11,0,966,192,1,0,0,0,967,968,3,165,75,0,968, - 969,1,0,0,0,969,970,6,89,13,0,970,194,1,0,0,0,971,972,3,167,76,0,972,973, - 1,0,0,0,973,974,6,90,16,0,974,196,1,0,0,0,975,976,3,337,161,0,976,977,1, - 0,0,0,977,978,6,91,17,0,978,198,1,0,0,0,979,980,3,101,43,0,980,981,1,0, - 0,0,981,982,6,92,18,0,982,200,1,0,0,0,983,984,3,97,41,0,984,985,1,0,0,0, - 985,986,6,93,19,0,986,202,1,0,0,0,987,988,7,16,0,0,988,989,7,3,0,0,989, - 990,7,5,0,0,990,991,7,12,0,0,991,992,7,0,0,0,992,993,7,12,0,0,993,994,7, - 5,0,0,994,995,7,12,0,0,995,204,1,0,0,0,996,1000,8,32,0,0,997,998,5,47,0, - 0,998,1000,8,33,0,0,999,996,1,0,0,0,999,997,1,0,0,0,1000,206,1,0,0,0,1001, - 1003,3,205,95,0,1002,1001,1,0,0,0,1003,1004,1,0,0,0,1004,1002,1,0,0,0,1004, - 1005,1,0,0,0,1005,208,1,0,0,0,1006,1007,3,207,96,0,1007,1008,1,0,0,0,1008, - 1009,6,97,20,0,1009,210,1,0,0,0,1010,1011,3,85,35,0,1011,1012,1,0,0,0,1012, - 1013,6,98,21,0,1013,212,1,0,0,0,1014,1015,3,57,21,0,1015,1016,1,0,0,0,1016, - 1017,6,99,10,0,1017,214,1,0,0,0,1018,1019,3,59,22,0,1019,1020,1,0,0,0,1020, - 1021,6,100,10,0,1021,216,1,0,0,0,1022,1023,3,61,23,0,1023,1024,1,0,0,0, - 1024,1025,6,101,10,0,1025,218,1,0,0,0,1026,1027,3,63,24,0,1027,1028,1,0, - 0,0,1028,1029,6,102,15,0,1029,1030,6,102,11,0,1030,220,1,0,0,0,1031,1032, - 3,105,45,0,1032,1033,1,0,0,0,1033,1034,6,103,22,0,1034,222,1,0,0,0,1035, - 1036,3,101,43,0,1036,1037,1,0,0,0,1037,1038,6,104,18,0,1038,224,1,0,0,0, - 1039,1040,3,129,57,0,1040,1041,1,0,0,0,1041,1042,6,105,23,0,1042,226,1, - 0,0,0,1043,1044,3,163,74,0,1044,1045,1,0,0,0,1045,1046,6,106,24,0,1046, - 228,1,0,0,0,1047,1052,3,67,26,0,1048,1052,3,65,25,0,1049,1052,3,81,33,0, - 1050,1052,3,155,70,0,1051,1047,1,0,0,0,1051,1048,1,0,0,0,1051,1049,1,0, - 0,0,1051,1050,1,0,0,0,1052,230,1,0,0,0,1053,1056,3,67,26,0,1054,1056,3, - 155,70,0,1055,1053,1,0,0,0,1055,1054,1,0,0,0,1056,1060,1,0,0,0,1057,1059, - 3,229,107,0,1058,1057,1,0,0,0,1059,1062,1,0,0,0,1060,1058,1,0,0,0,1060, - 1061,1,0,0,0,1061,1073,1,0,0,0,1062,1060,1,0,0,0,1063,1066,3,81,33,0,1064, - 1066,3,75,30,0,1065,1063,1,0,0,0,1065,1064,1,0,0,0,1066,1068,1,0,0,0,1067, - 1069,3,229,107,0,1068,1067,1,0,0,0,1069,1070,1,0,0,0,1070,1068,1,0,0,0, - 1070,1071,1,0,0,0,1071,1073,1,0,0,0,1072,1055,1,0,0,0,1072,1065,1,0,0,0, - 1073,232,1,0,0,0,1074,1077,3,231,108,0,1075,1077,3,171,78,0,1076,1074,1, - 0,0,0,1076,1075,1,0,0,0,1077,1078,1,0,0,0,1078,1076,1,0,0,0,1078,1079,1, - 0,0,0,1079,234,1,0,0,0,1080,1081,3,57,21,0,1081,1082,1,0,0,0,1082,1083, - 6,110,10,0,1083,236,1,0,0,0,1084,1085,3,59,22,0,1085,1086,1,0,0,0,1086, - 1087,6,111,10,0,1087,238,1,0,0,0,1088,1089,3,61,23,0,1089,1090,1,0,0,0, - 1090,1091,6,112,10,0,1091,240,1,0,0,0,1092,1093,3,63,24,0,1093,1094,1,0, - 0,0,1094,1095,6,113,15,0,1095,1096,6,113,11,0,1096,242,1,0,0,0,1097,1098, - 3,97,41,0,1098,1099,1,0,0,0,1099,1100,6,114,19,0,1100,244,1,0,0,0,1101, - 1102,3,101,43,0,1102,1103,1,0,0,0,1103,1104,6,115,18,0,1104,246,1,0,0,0, - 1105,1106,3,105,45,0,1106,1107,1,0,0,0,1107,1108,6,116,22,0,1108,248,1, - 0,0,0,1109,1110,3,129,57,0,1110,1111,1,0,0,0,1111,1112,6,117,23,0,1112, - 250,1,0,0,0,1113,1114,3,163,74,0,1114,1115,1,0,0,0,1115,1116,6,118,24,0, - 1116,252,1,0,0,0,1117,1118,7,12,0,0,1118,1119,7,2,0,0,1119,254,1,0,0,0, - 1120,1121,3,233,109,0,1121,1122,1,0,0,0,1122,1123,6,120,25,0,1123,256,1, - 0,0,0,1124,1125,3,57,21,0,1125,1126,1,0,0,0,1126,1127,6,121,10,0,1127,258, - 1,0,0,0,1128,1129,3,59,22,0,1129,1130,1,0,0,0,1130,1131,6,122,10,0,1131, - 260,1,0,0,0,1132,1133,3,61,23,0,1133,1134,1,0,0,0,1134,1135,6,123,10,0, - 1135,262,1,0,0,0,1136,1137,3,63,24,0,1137,1138,1,0,0,0,1138,1139,6,124, - 15,0,1139,1140,6,124,11,0,1140,264,1,0,0,0,1141,1142,3,165,75,0,1142,1143, - 1,0,0,0,1143,1144,6,125,13,0,1144,1145,6,125,26,0,1145,266,1,0,0,0,1146, - 1147,7,7,0,0,1147,1148,7,9,0,0,1148,1149,1,0,0,0,1149,1150,6,126,27,0,1150, - 268,1,0,0,0,1151,1152,7,19,0,0,1152,1153,7,1,0,0,1153,1154,7,5,0,0,1154, - 1155,7,10,0,0,1155,1156,1,0,0,0,1156,1157,6,127,27,0,1157,270,1,0,0,0,1158, - 1159,8,34,0,0,1159,272,1,0,0,0,1160,1162,3,271,128,0,1161,1160,1,0,0,0, - 1162,1163,1,0,0,0,1163,1161,1,0,0,0,1163,1164,1,0,0,0,1164,1165,1,0,0,0, - 1165,1166,3,337,161,0,1166,1168,1,0,0,0,1167,1161,1,0,0,0,1167,1168,1,0, - 0,0,1168,1170,1,0,0,0,1169,1171,3,271,128,0,1170,1169,1,0,0,0,1171,1172, - 1,0,0,0,1172,1170,1,0,0,0,1172,1173,1,0,0,0,1173,274,1,0,0,0,1174,1175, - 3,273,129,0,1175,1176,1,0,0,0,1176,1177,6,130,28,0,1177,276,1,0,0,0,1178, - 1179,3,57,21,0,1179,1180,1,0,0,0,1180,1181,6,131,10,0,1181,278,1,0,0,0, - 1182,1183,3,59,22,0,1183,1184,1,0,0,0,1184,1185,6,132,10,0,1185,280,1,0, - 0,0,1186,1187,3,61,23,0,1187,1188,1,0,0,0,1188,1189,6,133,10,0,1189,282, - 1,0,0,0,1190,1191,3,63,24,0,1191,1192,1,0,0,0,1192,1193,6,134,15,0,1193, - 1194,6,134,11,0,1194,1195,6,134,11,0,1195,284,1,0,0,0,1196,1197,3,97,41, - 0,1197,1198,1,0,0,0,1198,1199,6,135,19,0,1199,286,1,0,0,0,1200,1201,3,101, - 43,0,1201,1202,1,0,0,0,1202,1203,6,136,18,0,1203,288,1,0,0,0,1204,1205, - 3,105,45,0,1205,1206,1,0,0,0,1206,1207,6,137,22,0,1207,290,1,0,0,0,1208, - 1209,3,269,127,0,1209,1210,1,0,0,0,1210,1211,6,138,29,0,1211,292,1,0,0, - 0,1212,1213,3,233,109,0,1213,1214,1,0,0,0,1214,1215,6,139,25,0,1215,294, - 1,0,0,0,1216,1217,3,173,79,0,1217,1218,1,0,0,0,1218,1219,6,140,30,0,1219, - 296,1,0,0,0,1220,1221,3,129,57,0,1221,1222,1,0,0,0,1222,1223,6,141,23,0, - 1223,298,1,0,0,0,1224,1225,3,163,74,0,1225,1226,1,0,0,0,1226,1227,6,142, - 24,0,1227,300,1,0,0,0,1228,1229,3,57,21,0,1229,1230,1,0,0,0,1230,1231,6, - 143,10,0,1231,302,1,0,0,0,1232,1233,3,59,22,0,1233,1234,1,0,0,0,1234,1235, - 6,144,10,0,1235,304,1,0,0,0,1236,1237,3,61,23,0,1237,1238,1,0,0,0,1238, - 1239,6,145,10,0,1239,306,1,0,0,0,1240,1241,3,63,24,0,1241,1242,1,0,0,0, - 1242,1243,6,146,15,0,1243,1244,6,146,11,0,1244,308,1,0,0,0,1245,1246,3, - 105,45,0,1246,1247,1,0,0,0,1247,1248,6,147,22,0,1248,310,1,0,0,0,1249,1250, - 3,129,57,0,1250,1251,1,0,0,0,1251,1252,6,148,23,0,1252,312,1,0,0,0,1253, - 1254,3,163,74,0,1254,1255,1,0,0,0,1255,1256,6,149,24,0,1256,314,1,0,0,0, - 1257,1258,3,173,79,0,1258,1259,1,0,0,0,1259,1260,6,150,30,0,1260,316,1, - 0,0,0,1261,1262,3,169,77,0,1262,1263,1,0,0,0,1263,1264,6,151,31,0,1264, - 318,1,0,0,0,1265,1266,3,57,21,0,1266,1267,1,0,0,0,1267,1268,6,152,10,0, - 1268,320,1,0,0,0,1269,1270,3,59,22,0,1270,1271,1,0,0,0,1271,1272,6,153, - 10,0,1272,322,1,0,0,0,1273,1274,3,61,23,0,1274,1275,1,0,0,0,1275,1276,6, - 154,10,0,1276,324,1,0,0,0,1277,1278,3,63,24,0,1278,1279,1,0,0,0,1279,1280, - 6,155,15,0,1280,1281,6,155,11,0,1281,326,1,0,0,0,1282,1283,7,1,0,0,1283, - 1284,7,9,0,0,1284,1285,7,15,0,0,1285,1286,7,7,0,0,1286,328,1,0,0,0,1287, - 1288,3,57,21,0,1288,1289,1,0,0,0,1289,1290,6,157,10,0,1290,330,1,0,0,0, - 1291,1292,3,59,22,0,1292,1293,1,0,0,0,1293,1294,6,158,10,0,1294,332,1,0, - 0,0,1295,1296,3,61,23,0,1296,1297,1,0,0,0,1297,1298,6,159,10,0,1298,334, - 1,0,0,0,1299,1300,3,167,76,0,1300,1301,1,0,0,0,1301,1302,6,160,16,0,1302, - 1303,6,160,11,0,1303,336,1,0,0,0,1304,1305,5,58,0,0,1305,338,1,0,0,0,1306, - 1312,3,75,30,0,1307,1312,3,65,25,0,1308,1312,3,105,45,0,1309,1312,3,67, - 26,0,1310,1312,3,81,33,0,1311,1306,1,0,0,0,1311,1307,1,0,0,0,1311,1308, - 1,0,0,0,1311,1309,1,0,0,0,1311,1310,1,0,0,0,1312,1313,1,0,0,0,1313,1311, - 1,0,0,0,1313,1314,1,0,0,0,1314,340,1,0,0,0,1315,1316,3,57,21,0,1316,1317, - 1,0,0,0,1317,1318,6,163,10,0,1318,342,1,0,0,0,1319,1320,3,59,22,0,1320, - 1321,1,0,0,0,1321,1322,6,164,10,0,1322,344,1,0,0,0,1323,1324,3,61,23,0, - 1324,1325,1,0,0,0,1325,1326,6,165,10,0,1326,346,1,0,0,0,1327,1328,3,63, - 24,0,1328,1329,1,0,0,0,1329,1330,6,166,15,0,1330,1331,6,166,11,0,1331,348, - 1,0,0,0,1332,1333,3,337,161,0,1333,1334,1,0,0,0,1334,1335,6,167,17,0,1335, - 350,1,0,0,0,1336,1337,3,101,43,0,1337,1338,1,0,0,0,1338,1339,6,168,18,0, - 1339,352,1,0,0,0,1340,1341,3,105,45,0,1341,1342,1,0,0,0,1342,1343,6,169, - 22,0,1343,354,1,0,0,0,1344,1345,3,267,126,0,1345,1346,1,0,0,0,1346,1347, - 6,170,32,0,1347,1348,6,170,33,0,1348,356,1,0,0,0,1349,1350,3,207,96,0,1350, - 1351,1,0,0,0,1351,1352,6,171,20,0,1352,358,1,0,0,0,1353,1354,3,85,35,0, - 1354,1355,1,0,0,0,1355,1356,6,172,21,0,1356,360,1,0,0,0,1357,1358,3,57, - 21,0,1358,1359,1,0,0,0,1359,1360,6,173,10,0,1360,362,1,0,0,0,1361,1362, - 3,59,22,0,1362,1363,1,0,0,0,1363,1364,6,174,10,0,1364,364,1,0,0,0,1365, - 1366,3,61,23,0,1366,1367,1,0,0,0,1367,1368,6,175,10,0,1368,366,1,0,0,0, - 1369,1370,3,63,24,0,1370,1371,1,0,0,0,1371,1372,6,176,15,0,1372,1373,6, - 176,11,0,1373,1374,6,176,11,0,1374,368,1,0,0,0,1375,1376,3,101,43,0,1376, - 1377,1,0,0,0,1377,1378,6,177,18,0,1378,370,1,0,0,0,1379,1380,3,105,45,0, - 1380,1381,1,0,0,0,1381,1382,6,178,22,0,1382,372,1,0,0,0,1383,1384,3,233, - 109,0,1384,1385,1,0,0,0,1385,1386,6,179,25,0,1386,374,1,0,0,0,1387,1388, - 3,57,21,0,1388,1389,1,0,0,0,1389,1390,6,180,10,0,1390,376,1,0,0,0,1391, - 1392,3,59,22,0,1392,1393,1,0,0,0,1393,1394,6,181,10,0,1394,378,1,0,0,0, - 1395,1396,3,61,23,0,1396,1397,1,0,0,0,1397,1398,6,182,10,0,1398,380,1,0, - 0,0,1399,1400,3,63,24,0,1400,1401,1,0,0,0,1401,1402,6,183,15,0,1402,1403, - 6,183,11,0,1403,382,1,0,0,0,1404,1405,3,207,96,0,1405,1406,1,0,0,0,1406, - 1407,6,184,20,0,1407,1408,6,184,11,0,1408,1409,6,184,34,0,1409,384,1,0, - 0,0,1410,1411,3,85,35,0,1411,1412,1,0,0,0,1412,1413,6,185,21,0,1413,1414, - 6,185,11,0,1414,1415,6,185,34,0,1415,386,1,0,0,0,1416,1417,3,57,21,0,1417, - 1418,1,0,0,0,1418,1419,6,186,10,0,1419,388,1,0,0,0,1420,1421,3,59,22,0, - 1421,1422,1,0,0,0,1422,1423,6,187,10,0,1423,390,1,0,0,0,1424,1425,3,61, - 23,0,1425,1426,1,0,0,0,1426,1427,6,188,10,0,1427,392,1,0,0,0,1428,1429, - 3,337,161,0,1429,1430,1,0,0,0,1430,1431,6,189,17,0,1431,1432,6,189,11,0, - 1432,1433,6,189,9,0,1433,394,1,0,0,0,1434,1435,3,101,43,0,1435,1436,1,0, - 0,0,1436,1437,6,190,18,0,1437,1438,6,190,11,0,1438,1439,6,190,9,0,1439, - 396,1,0,0,0,1440,1441,3,57,21,0,1441,1442,1,0,0,0,1442,1443,6,191,10,0, - 1443,398,1,0,0,0,1444,1445,3,59,22,0,1445,1446,1,0,0,0,1446,1447,6,192, - 10,0,1447,400,1,0,0,0,1448,1449,3,61,23,0,1449,1450,1,0,0,0,1450,1451,6, - 193,10,0,1451,402,1,0,0,0,1452,1453,3,173,79,0,1453,1454,1,0,0,0,1454,1455, - 6,194,11,0,1455,1456,6,194,0,0,1456,1457,6,194,30,0,1457,404,1,0,0,0,1458, - 1459,3,169,77,0,1459,1460,1,0,0,0,1460,1461,6,195,11,0,1461,1462,6,195, - 0,0,1462,1463,6,195,31,0,1463,406,1,0,0,0,1464,1465,3,91,38,0,1465,1466, - 1,0,0,0,1466,1467,6,196,11,0,1467,1468,6,196,0,0,1468,1469,6,196,35,0,1469, - 408,1,0,0,0,1470,1471,3,63,24,0,1471,1472,1,0,0,0,1472,1473,6,197,15,0, - 1473,1474,6,197,11,0,1474,410,1,0,0,0,65,0,1,2,3,4,5,6,7,8,9,10,11,12,13, - 14,588,598,602,605,614,616,627,646,651,660,667,672,674,685,693,696,698, - 703,708,714,721,726,732,735,743,747,874,879,886,888,904,909,914,916,922, - 999,1004,1051,1055,1060,1065,1070,1072,1076,1078,1163,1167,1172,1311,1313, - 36,5,1,0,5,4,0,5,6,0,5,2,0,5,3,0,5,8,0,5,5,0,5,9,0,5,11,0,5,13,0,0,1,0, - 4,0,0,7,19,0,7,65,0,5,0,0,7,25,0,7,66,0,7,104,0,7,34,0,7,32,0,7,76,0,7, - 26,0,7,36,0,7,48,0,7,64,0,7,80,0,5,10,0,5,7,0,7,90,0,7,89,0,7,68,0,7,67, - 0,7,88,0,5,12,0,5,14,0,7,29,0]; + 18,1,18,1,19,4,19,578,8,19,11,19,12,19,579,1,19,1,19,1,20,1,20,1,20,1,20, + 5,20,588,8,20,10,20,12,20,591,9,20,1,20,3,20,594,8,20,1,20,3,20,597,8,20, + 1,20,1,20,1,21,1,21,1,21,1,21,1,21,5,21,606,8,21,10,21,12,21,609,9,21,1, + 21,1,21,1,21,1,21,1,21,1,22,4,22,617,8,22,11,22,12,22,618,1,22,1,22,1,23, + 1,23,1,23,1,23,1,24,1,24,1,25,1,25,1,26,1,26,1,26,1,27,1,27,1,28,1,28,3, + 28,638,8,28,1,28,4,28,641,8,28,11,28,12,28,642,1,29,1,29,1,30,1,30,1,31, + 1,31,1,31,3,31,652,8,31,1,32,1,32,1,33,1,33,1,33,3,33,659,8,33,1,34,1,34, + 1,34,5,34,664,8,34,10,34,12,34,667,9,34,1,34,1,34,1,34,1,34,1,34,1,34,5, + 34,675,8,34,10,34,12,34,678,9,34,1,34,1,34,1,34,1,34,1,34,3,34,685,8,34, + 1,34,3,34,688,8,34,3,34,690,8,34,1,35,4,35,693,8,35,11,35,12,35,694,1,36, + 4,36,698,8,36,11,36,12,36,699,1,36,1,36,5,36,704,8,36,10,36,12,36,707,9, + 36,1,36,1,36,4,36,711,8,36,11,36,12,36,712,1,36,4,36,716,8,36,11,36,12, + 36,717,1,36,1,36,5,36,722,8,36,10,36,12,36,725,9,36,3,36,727,8,36,1,36, + 1,36,1,36,1,36,4,36,733,8,36,11,36,12,36,734,1,36,1,36,3,36,739,8,36,1, + 37,1,37,1,37,1,38,1,38,1,38,1,38,1,39,1,39,1,39,1,39,1,40,1,40,1,41,1,41, + 1,41,1,42,1,42,1,43,1,43,1,43,1,43,1,43,1,44,1,44,1,45,1,45,1,45,1,45,1, + 45,1,45,1,46,1,46,1,46,1,46,1,46,1,46,1,47,1,47,1,47,1,48,1,48,1,48,1,49, + 1,49,1,49,1,49,1,49,1,50,1,50,1,50,1,50,1,50,1,51,1,51,1,52,1,52,1,52,1, + 52,1,53,1,53,1,53,1,53,1,53,1,54,1,54,1,54,1,54,1,54,1,54,1,55,1,55,1,55, + 1,56,1,56,1,57,1,57,1,57,1,57,1,57,1,57,1,58,1,58,1,59,1,59,1,59,1,59,1, + 59,1,60,1,60,1,60,1,61,1,61,1,61,1,62,1,62,1,62,1,63,1,63,1,64,1,64,1,64, + 1,65,1,65,1,66,1,66,1,66,1,67,1,67,1,68,1,68,1,69,1,69,1,70,1,70,1,71,1, + 71,1,72,1,72,1,72,1,72,1,72,1,72,1,73,1,73,1,73,1,73,1,74,1,74,1,74,3,74, + 871,8,74,1,74,5,74,874,8,74,10,74,12,74,877,9,74,1,74,1,74,4,74,881,8,74, + 11,74,12,74,882,3,74,885,8,74,1,75,1,75,1,75,1,75,1,75,1,76,1,76,1,76,1, + 76,1,76,1,77,1,77,5,77,899,8,77,10,77,12,77,902,9,77,1,77,1,77,3,77,906, + 8,77,1,77,4,77,909,8,77,11,77,12,77,910,3,77,913,8,77,1,78,1,78,4,78,917, + 8,78,11,78,12,78,918,1,78,1,78,1,79,1,79,1,80,1,80,1,80,1,80,1,81,1,81, + 1,81,1,81,1,82,1,82,1,82,1,82,1,83,1,83,1,83,1,83,1,83,1,84,1,84,1,84,1, + 84,1,84,1,85,1,85,1,85,1,85,1,86,1,86,1,86,1,86,1,87,1,87,1,87,1,87,1,88, + 1,88,1,88,1,88,1,88,1,89,1,89,1,89,1,89,1,90,1,90,1,90,1,90,1,91,1,91,1, + 91,1,91,1,92,1,92,1,92,1,92,1,93,1,93,1,93,1,93,1,94,1,94,1,94,1,94,1,94, + 1,94,1,94,1,94,1,94,1,95,1,95,1,95,3,95,996,8,95,1,96,4,96,999,8,96,11, + 96,12,96,1000,1,97,1,97,1,97,1,97,1,98,1,98,1,98,1,98,1,99,1,99,1,99,1, + 99,1,100,1,100,1,100,1,100,1,101,1,101,1,101,1,101,1,102,1,102,1,102,1, + 102,1,102,1,103,1,103,1,103,1,103,1,104,1,104,1,104,1,104,1,105,1,105,1, + 105,1,105,1,105,1,106,1,106,1,106,1,106,1,106,1,107,1,107,1,107,1,107,3, + 107,1050,8,107,1,108,1,108,3,108,1054,8,108,1,108,5,108,1057,8,108,10,108, + 12,108,1060,9,108,1,108,1,108,3,108,1064,8,108,1,108,4,108,1067,8,108,11, + 108,12,108,1068,3,108,1071,8,108,1,109,1,109,4,109,1075,8,109,11,109,12, + 109,1076,1,110,1,110,1,110,1,110,1,111,1,111,1,111,1,111,1,112,1,112,1, + 112,1,112,1,113,1,113,1,113,1,113,1,113,1,114,1,114,1,114,1,114,1,115,1, + 115,1,115,1,115,1,116,1,116,1,116,1,116,1,117,1,117,1,117,1,117,1,117,1, + 118,1,118,1,118,1,118,1,118,1,119,1,119,1,119,1,120,1,120,1,120,1,120,1, + 121,1,121,1,121,1,121,1,122,1,122,1,122,1,122,1,123,1,123,1,123,1,123,1, + 124,1,124,1,124,1,124,1,124,1,125,1,125,1,125,1,125,1,125,1,126,1,126,1, + 126,1,126,1,126,1,127,1,127,1,127,1,127,1,127,1,127,1,127,1,128,1,128,1, + 129,4,129,1162,8,129,11,129,12,129,1163,1,129,1,129,3,129,1168,8,129,1, + 129,4,129,1171,8,129,11,129,12,129,1172,1,130,1,130,1,130,1,130,1,131,1, + 131,1,131,1,131,1,132,1,132,1,132,1,132,1,133,1,133,1,133,1,133,1,134,1, + 134,1,134,1,134,1,134,1,134,1,135,1,135,1,135,1,135,1,136,1,136,1,136,1, + 136,1,137,1,137,1,137,1,137,1,138,1,138,1,138,1,138,1,139,1,139,1,139,1, + 139,1,140,1,140,1,140,1,140,1,141,1,141,1,141,1,141,1,141,1,142,1,142,1, + 142,1,142,1,142,1,143,1,143,1,143,1,143,1,144,1,144,1,144,1,144,1,145,1, + 145,1,145,1,145,1,146,1,146,1,146,1,146,1,146,1,147,1,147,1,147,1,147,1, + 148,1,148,1,148,1,148,1,148,1,149,1,149,1,149,1,149,1,149,1,150,1,150,1, + 150,1,150,1,151,1,151,1,151,1,151,1,152,1,152,1,152,1,152,1,153,1,153,1, + 153,1,153,1,154,1,154,1,154,1,154,1,155,1,155,1,155,1,155,1,155,1,156,1, + 156,1,156,1,156,1,156,1,157,1,157,1,157,1,157,1,158,1,158,1,158,1,158,1, + 159,1,159,1,159,1,159,1,160,1,160,1,160,1,160,1,160,1,161,1,161,1,162,1, + 162,1,162,1,162,1,162,4,162,1316,8,162,11,162,12,162,1317,1,163,1,163,1, + 163,1,163,1,164,1,164,1,164,1,164,1,165,1,165,1,165,1,165,1,166,1,166,1, + 166,1,166,1,166,1,167,1,167,1,167,1,167,1,168,1,168,1,168,1,168,1,169,1, + 169,1,169,1,169,1,170,1,170,1,170,1,170,1,170,1,171,1,171,1,171,1,171,1, + 172,1,172,1,172,1,172,1,173,1,173,1,173,1,173,1,174,1,174,1,174,1,174,1, + 175,1,175,1,175,1,175,1,176,1,176,1,176,1,176,1,176,1,176,1,177,1,177,1, + 177,1,177,1,178,1,178,1,178,1,178,1,179,1,179,1,179,1,179,1,180,1,180,1, + 180,1,180,1,181,1,181,1,181,1,181,1,182,1,182,1,182,1,182,1,183,1,183,1, + 183,1,183,1,183,1,184,1,184,1,184,1,184,1,184,1,184,1,185,1,185,1,185,1, + 185,1,185,1,185,1,186,1,186,1,186,1,186,1,187,1,187,1,187,1,187,1,188,1, + 188,1,188,1,188,1,189,1,189,1,189,1,189,1,189,1,189,1,190,1,190,1,190,1, + 190,1,190,1,190,1,191,1,191,1,191,1,191,1,192,1,192,1,192,1,192,1,193,1, + 193,1,193,1,193,1,194,1,194,1,194,1,194,1,194,1,194,1,195,1,195,1,195,1, + 195,1,195,1,195,1,196,1,196,1,196,1,196,1,196,1,196,1,197,1,197,1,197,1, + 197,1,197,2,607,676,0,198,15,1,17,2,19,3,21,4,23,5,25,6,27,7,29,8,31,9, + 33,10,35,11,37,12,39,13,41,14,43,15,45,16,47,17,49,18,51,19,53,20,55,21, + 57,22,59,23,61,24,63,0,65,0,67,0,69,0,71,0,73,0,75,0,77,0,79,0,81,0,83, + 25,85,26,87,27,89,28,91,29,93,30,95,31,97,32,99,33,101,34,103,35,105,36, + 107,37,109,38,111,39,113,40,115,41,117,42,119,43,121,44,123,45,125,46,127, + 47,129,48,131,49,133,50,135,51,137,52,139,53,141,54,143,55,145,56,147,57, + 149,58,151,59,153,60,155,61,157,62,159,63,161,0,163,64,165,65,167,66,169, + 67,171,0,173,68,175,69,177,70,179,71,181,0,183,0,185,72,187,73,189,74,191, + 0,193,0,195,0,197,0,199,0,201,0,203,75,205,0,207,76,209,0,211,0,213,77, + 215,78,217,79,219,0,221,0,223,0,225,0,227,0,229,0,231,0,233,80,235,81,237, + 82,239,83,241,0,243,0,245,0,247,0,249,0,251,0,253,84,255,0,257,85,259,86, + 261,87,263,0,265,0,267,88,269,89,271,0,273,90,275,0,277,91,279,92,281,93, + 283,0,285,0,287,0,289,0,291,0,293,0,295,0,297,0,299,0,301,94,303,95,305, + 96,307,0,309,0,311,0,313,0,315,0,317,0,319,97,321,98,323,99,325,0,327,100, + 329,101,331,102,333,103,335,0,337,104,339,105,341,106,343,107,345,108,347, + 0,349,0,351,0,353,0,355,0,357,0,359,0,361,109,363,110,365,111,367,0,369, + 0,371,0,373,0,375,112,377,113,379,114,381,0,383,0,385,0,387,115,389,116, + 391,117,393,0,395,0,397,118,399,119,401,120,403,0,405,0,407,0,409,0,15, + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,35,2,0,68,68,100,100,2,0,73,73,105,105, + 2,0,83,83,115,115,2,0,69,69,101,101,2,0,67,67,99,99,2,0,84,84,116,116,2, + 0,82,82,114,114,2,0,79,79,111,111,2,0,80,80,112,112,2,0,78,78,110,110,2, + 0,72,72,104,104,2,0,86,86,118,118,2,0,65,65,97,97,2,0,76,76,108,108,2,0, + 88,88,120,120,2,0,70,70,102,102,2,0,77,77,109,109,2,0,71,71,103,103,2,0, + 75,75,107,107,2,0,87,87,119,119,2,0,85,85,117,117,6,0,9,10,13,13,32,32, + 47,47,91,91,93,93,2,0,10,10,13,13,3,0,9,10,13,13,32,32,1,0,48,57,2,0,65, + 90,97,122,8,0,34,34,78,78,82,82,84,84,92,92,110,110,114,114,116,116,4,0, + 10,10,13,13,34,34,92,92,2,0,43,43,45,45,1,0,96,96,2,0,66,66,98,98,2,0,89, + 89,121,121,11,0,9,10,13,13,32,32,34,34,44,44,47,47,58,58,61,61,91,91,93, + 93,124,124,2,0,42,42,47,47,11,0,9,10,13,13,32,32,34,35,44,44,47,47,58,58, + 60,60,62,63,92,92,124,124,1507,0,15,1,0,0,0,0,17,1,0,0,0,0,19,1,0,0,0,0, + 21,1,0,0,0,0,23,1,0,0,0,0,25,1,0,0,0,0,27,1,0,0,0,0,29,1,0,0,0,0,31,1,0, + 0,0,0,33,1,0,0,0,0,35,1,0,0,0,0,37,1,0,0,0,0,39,1,0,0,0,0,41,1,0,0,0,0, + 43,1,0,0,0,0,45,1,0,0,0,0,47,1,0,0,0,0,49,1,0,0,0,0,51,1,0,0,0,0,53,1,0, + 0,0,0,55,1,0,0,0,0,57,1,0,0,0,0,59,1,0,0,0,1,61,1,0,0,0,1,83,1,0,0,0,1, + 85,1,0,0,0,1,87,1,0,0,0,1,89,1,0,0,0,1,91,1,0,0,0,1,93,1,0,0,0,1,95,1,0, + 0,0,1,97,1,0,0,0,1,99,1,0,0,0,1,101,1,0,0,0,1,103,1,0,0,0,1,105,1,0,0,0, + 1,107,1,0,0,0,1,109,1,0,0,0,1,111,1,0,0,0,1,113,1,0,0,0,1,115,1,0,0,0,1, + 117,1,0,0,0,1,119,1,0,0,0,1,121,1,0,0,0,1,123,1,0,0,0,1,125,1,0,0,0,1,127, + 1,0,0,0,1,129,1,0,0,0,1,131,1,0,0,0,1,133,1,0,0,0,1,135,1,0,0,0,1,137,1, + 0,0,0,1,139,1,0,0,0,1,141,1,0,0,0,1,143,1,0,0,0,1,145,1,0,0,0,1,147,1,0, + 0,0,1,149,1,0,0,0,1,151,1,0,0,0,1,153,1,0,0,0,1,155,1,0,0,0,1,157,1,0,0, + 0,1,159,1,0,0,0,1,161,1,0,0,0,1,163,1,0,0,0,1,165,1,0,0,0,1,167,1,0,0,0, + 1,169,1,0,0,0,1,173,1,0,0,0,1,175,1,0,0,0,1,177,1,0,0,0,1,179,1,0,0,0,2, + 181,1,0,0,0,2,183,1,0,0,0,2,185,1,0,0,0,2,187,1,0,0,0,2,189,1,0,0,0,3,191, + 1,0,0,0,3,193,1,0,0,0,3,195,1,0,0,0,3,197,1,0,0,0,3,199,1,0,0,0,3,201,1, + 0,0,0,3,203,1,0,0,0,3,207,1,0,0,0,3,209,1,0,0,0,3,211,1,0,0,0,3,213,1,0, + 0,0,3,215,1,0,0,0,3,217,1,0,0,0,4,219,1,0,0,0,4,221,1,0,0,0,4,223,1,0,0, + 0,4,225,1,0,0,0,4,227,1,0,0,0,4,233,1,0,0,0,4,235,1,0,0,0,4,237,1,0,0,0, + 4,239,1,0,0,0,5,241,1,0,0,0,5,243,1,0,0,0,5,245,1,0,0,0,5,247,1,0,0,0,5, + 249,1,0,0,0,5,251,1,0,0,0,5,253,1,0,0,0,5,255,1,0,0,0,5,257,1,0,0,0,5,259, + 1,0,0,0,5,261,1,0,0,0,6,263,1,0,0,0,6,265,1,0,0,0,6,267,1,0,0,0,6,269,1, + 0,0,0,6,273,1,0,0,0,6,275,1,0,0,0,6,277,1,0,0,0,6,279,1,0,0,0,6,281,1,0, + 0,0,7,283,1,0,0,0,7,285,1,0,0,0,7,287,1,0,0,0,7,289,1,0,0,0,7,291,1,0,0, + 0,7,293,1,0,0,0,7,295,1,0,0,0,7,297,1,0,0,0,7,299,1,0,0,0,7,301,1,0,0,0, + 7,303,1,0,0,0,7,305,1,0,0,0,8,307,1,0,0,0,8,309,1,0,0,0,8,311,1,0,0,0,8, + 313,1,0,0,0,8,315,1,0,0,0,8,317,1,0,0,0,8,319,1,0,0,0,8,321,1,0,0,0,8,323, + 1,0,0,0,9,325,1,0,0,0,9,327,1,0,0,0,9,329,1,0,0,0,9,331,1,0,0,0,9,333,1, + 0,0,0,10,335,1,0,0,0,10,337,1,0,0,0,10,339,1,0,0,0,10,341,1,0,0,0,10,343, + 1,0,0,0,10,345,1,0,0,0,11,347,1,0,0,0,11,349,1,0,0,0,11,351,1,0,0,0,11, + 353,1,0,0,0,11,355,1,0,0,0,11,357,1,0,0,0,11,359,1,0,0,0,11,361,1,0,0,0, + 11,363,1,0,0,0,11,365,1,0,0,0,12,367,1,0,0,0,12,369,1,0,0,0,12,371,1,0, + 0,0,12,373,1,0,0,0,12,375,1,0,0,0,12,377,1,0,0,0,12,379,1,0,0,0,13,381, + 1,0,0,0,13,383,1,0,0,0,13,385,1,0,0,0,13,387,1,0,0,0,13,389,1,0,0,0,13, + 391,1,0,0,0,14,393,1,0,0,0,14,395,1,0,0,0,14,397,1,0,0,0,14,399,1,0,0,0, + 14,401,1,0,0,0,14,403,1,0,0,0,14,405,1,0,0,0,14,407,1,0,0,0,14,409,1,0, + 0,0,15,411,1,0,0,0,17,421,1,0,0,0,19,428,1,0,0,0,21,437,1,0,0,0,23,444, + 1,0,0,0,25,454,1,0,0,0,27,461,1,0,0,0,29,468,1,0,0,0,31,475,1,0,0,0,33, + 483,1,0,0,0,35,495,1,0,0,0,37,504,1,0,0,0,39,510,1,0,0,0,41,517,1,0,0,0, + 43,524,1,0,0,0,45,532,1,0,0,0,47,540,1,0,0,0,49,555,1,0,0,0,51,565,1,0, + 0,0,53,577,1,0,0,0,55,583,1,0,0,0,57,600,1,0,0,0,59,616,1,0,0,0,61,622, + 1,0,0,0,63,626,1,0,0,0,65,628,1,0,0,0,67,630,1,0,0,0,69,633,1,0,0,0,71, + 635,1,0,0,0,73,644,1,0,0,0,75,646,1,0,0,0,77,651,1,0,0,0,79,653,1,0,0,0, + 81,658,1,0,0,0,83,689,1,0,0,0,85,692,1,0,0,0,87,738,1,0,0,0,89,740,1,0, + 0,0,91,743,1,0,0,0,93,747,1,0,0,0,95,751,1,0,0,0,97,753,1,0,0,0,99,756, + 1,0,0,0,101,758,1,0,0,0,103,763,1,0,0,0,105,765,1,0,0,0,107,771,1,0,0,0, + 109,777,1,0,0,0,111,780,1,0,0,0,113,783,1,0,0,0,115,788,1,0,0,0,117,793, + 1,0,0,0,119,795,1,0,0,0,121,799,1,0,0,0,123,804,1,0,0,0,125,810,1,0,0,0, + 127,813,1,0,0,0,129,815,1,0,0,0,131,821,1,0,0,0,133,823,1,0,0,0,135,828, + 1,0,0,0,137,831,1,0,0,0,139,834,1,0,0,0,141,837,1,0,0,0,143,839,1,0,0,0, + 145,842,1,0,0,0,147,844,1,0,0,0,149,847,1,0,0,0,151,849,1,0,0,0,153,851, + 1,0,0,0,155,853,1,0,0,0,157,855,1,0,0,0,159,857,1,0,0,0,161,863,1,0,0,0, + 163,884,1,0,0,0,165,886,1,0,0,0,167,891,1,0,0,0,169,912,1,0,0,0,171,914, + 1,0,0,0,173,922,1,0,0,0,175,924,1,0,0,0,177,928,1,0,0,0,179,932,1,0,0,0, + 181,936,1,0,0,0,183,941,1,0,0,0,185,946,1,0,0,0,187,950,1,0,0,0,189,954, + 1,0,0,0,191,958,1,0,0,0,193,963,1,0,0,0,195,967,1,0,0,0,197,971,1,0,0,0, + 199,975,1,0,0,0,201,979,1,0,0,0,203,983,1,0,0,0,205,995,1,0,0,0,207,998, + 1,0,0,0,209,1002,1,0,0,0,211,1006,1,0,0,0,213,1010,1,0,0,0,215,1014,1,0, + 0,0,217,1018,1,0,0,0,219,1022,1,0,0,0,221,1027,1,0,0,0,223,1031,1,0,0,0, + 225,1035,1,0,0,0,227,1040,1,0,0,0,229,1049,1,0,0,0,231,1070,1,0,0,0,233, + 1074,1,0,0,0,235,1078,1,0,0,0,237,1082,1,0,0,0,239,1086,1,0,0,0,241,1090, + 1,0,0,0,243,1095,1,0,0,0,245,1099,1,0,0,0,247,1103,1,0,0,0,249,1107,1,0, + 0,0,251,1112,1,0,0,0,253,1117,1,0,0,0,255,1120,1,0,0,0,257,1124,1,0,0,0, + 259,1128,1,0,0,0,261,1132,1,0,0,0,263,1136,1,0,0,0,265,1141,1,0,0,0,267, + 1146,1,0,0,0,269,1151,1,0,0,0,271,1158,1,0,0,0,273,1167,1,0,0,0,275,1174, + 1,0,0,0,277,1178,1,0,0,0,279,1182,1,0,0,0,281,1186,1,0,0,0,283,1190,1,0, + 0,0,285,1196,1,0,0,0,287,1200,1,0,0,0,289,1204,1,0,0,0,291,1208,1,0,0,0, + 293,1212,1,0,0,0,295,1216,1,0,0,0,297,1220,1,0,0,0,299,1225,1,0,0,0,301, + 1230,1,0,0,0,303,1234,1,0,0,0,305,1238,1,0,0,0,307,1242,1,0,0,0,309,1247, + 1,0,0,0,311,1251,1,0,0,0,313,1256,1,0,0,0,315,1261,1,0,0,0,317,1265,1,0, + 0,0,319,1269,1,0,0,0,321,1273,1,0,0,0,323,1277,1,0,0,0,325,1281,1,0,0,0, + 327,1286,1,0,0,0,329,1291,1,0,0,0,331,1295,1,0,0,0,333,1299,1,0,0,0,335, + 1303,1,0,0,0,337,1308,1,0,0,0,339,1315,1,0,0,0,341,1319,1,0,0,0,343,1323, + 1,0,0,0,345,1327,1,0,0,0,347,1331,1,0,0,0,349,1336,1,0,0,0,351,1340,1,0, + 0,0,353,1344,1,0,0,0,355,1348,1,0,0,0,357,1353,1,0,0,0,359,1357,1,0,0,0, + 361,1361,1,0,0,0,363,1365,1,0,0,0,365,1369,1,0,0,0,367,1373,1,0,0,0,369, + 1379,1,0,0,0,371,1383,1,0,0,0,373,1387,1,0,0,0,375,1391,1,0,0,0,377,1395, + 1,0,0,0,379,1399,1,0,0,0,381,1403,1,0,0,0,383,1408,1,0,0,0,385,1414,1,0, + 0,0,387,1420,1,0,0,0,389,1424,1,0,0,0,391,1428,1,0,0,0,393,1432,1,0,0,0, + 395,1438,1,0,0,0,397,1444,1,0,0,0,399,1448,1,0,0,0,401,1452,1,0,0,0,403, + 1456,1,0,0,0,405,1462,1,0,0,0,407,1468,1,0,0,0,409,1474,1,0,0,0,411,412, + 7,0,0,0,412,413,7,1,0,0,413,414,7,2,0,0,414,415,7,2,0,0,415,416,7,3,0,0, + 416,417,7,4,0,0,417,418,7,5,0,0,418,419,1,0,0,0,419,420,6,0,0,0,420,16, + 1,0,0,0,421,422,7,0,0,0,422,423,7,6,0,0,423,424,7,7,0,0,424,425,7,8,0,0, + 425,426,1,0,0,0,426,427,6,1,1,0,427,18,1,0,0,0,428,429,7,3,0,0,429,430, + 7,9,0,0,430,431,7,6,0,0,431,432,7,1,0,0,432,433,7,4,0,0,433,434,7,10,0, + 0,434,435,1,0,0,0,435,436,6,2,2,0,436,20,1,0,0,0,437,438,7,3,0,0,438,439, + 7,11,0,0,439,440,7,12,0,0,440,441,7,13,0,0,441,442,1,0,0,0,442,443,6,3, + 0,0,443,22,1,0,0,0,444,445,7,3,0,0,445,446,7,14,0,0,446,447,7,8,0,0,447, + 448,7,13,0,0,448,449,7,12,0,0,449,450,7,1,0,0,450,451,7,9,0,0,451,452,1, + 0,0,0,452,453,6,4,3,0,453,24,1,0,0,0,454,455,7,15,0,0,455,456,7,6,0,0,456, + 457,7,7,0,0,457,458,7,16,0,0,458,459,1,0,0,0,459,460,6,5,4,0,460,26,1,0, + 0,0,461,462,7,17,0,0,462,463,7,6,0,0,463,464,7,7,0,0,464,465,7,18,0,0,465, + 466,1,0,0,0,466,467,6,6,0,0,467,28,1,0,0,0,468,469,7,18,0,0,469,470,7,3, + 0,0,470,471,7,3,0,0,471,472,7,8,0,0,472,473,1,0,0,0,473,474,6,7,1,0,474, + 30,1,0,0,0,475,476,7,13,0,0,476,477,7,1,0,0,477,478,7,16,0,0,478,479,7, + 1,0,0,479,480,7,5,0,0,480,481,1,0,0,0,481,482,6,8,0,0,482,32,1,0,0,0,483, + 484,7,16,0,0,484,485,7,11,0,0,485,486,5,95,0,0,486,487,7,3,0,0,487,488, + 7,14,0,0,488,489,7,8,0,0,489,490,7,12,0,0,490,491,7,9,0,0,491,492,7,0,0, + 0,492,493,1,0,0,0,493,494,6,9,5,0,494,34,1,0,0,0,495,496,7,6,0,0,496,497, + 7,3,0,0,497,498,7,9,0,0,498,499,7,12,0,0,499,500,7,16,0,0,500,501,7,3,0, + 0,501,502,1,0,0,0,502,503,6,10,6,0,503,36,1,0,0,0,504,505,7,6,0,0,505,506, + 7,7,0,0,506,507,7,19,0,0,507,508,1,0,0,0,508,509,6,11,0,0,509,38,1,0,0, + 0,510,511,7,2,0,0,511,512,7,10,0,0,512,513,7,7,0,0,513,514,7,19,0,0,514, + 515,1,0,0,0,515,516,6,12,7,0,516,40,1,0,0,0,517,518,7,2,0,0,518,519,7,7, + 0,0,519,520,7,6,0,0,520,521,7,5,0,0,521,522,1,0,0,0,522,523,6,13,0,0,523, + 42,1,0,0,0,524,525,7,2,0,0,525,526,7,5,0,0,526,527,7,12,0,0,527,528,7,5, + 0,0,528,529,7,2,0,0,529,530,1,0,0,0,530,531,6,14,0,0,531,44,1,0,0,0,532, + 533,7,19,0,0,533,534,7,10,0,0,534,535,7,3,0,0,535,536,7,6,0,0,536,537,7, + 3,0,0,537,538,1,0,0,0,538,539,6,15,0,0,539,46,1,0,0,0,540,541,4,16,0,0, + 541,542,7,1,0,0,542,543,7,9,0,0,543,544,7,13,0,0,544,545,7,1,0,0,545,546, + 7,9,0,0,546,547,7,3,0,0,547,548,7,2,0,0,548,549,7,5,0,0,549,550,7,12,0, + 0,550,551,7,5,0,0,551,552,7,2,0,0,552,553,1,0,0,0,553,554,6,16,0,0,554, + 48,1,0,0,0,555,556,4,17,1,0,556,557,7,13,0,0,557,558,7,7,0,0,558,559,7, + 7,0,0,559,560,7,18,0,0,560,561,7,20,0,0,561,562,7,8,0,0,562,563,1,0,0,0, + 563,564,6,17,8,0,564,50,1,0,0,0,565,566,4,18,2,0,566,567,7,16,0,0,567,568, + 7,3,0,0,568,569,7,5,0,0,569,570,7,6,0,0,570,571,7,1,0,0,571,572,7,4,0,0, + 572,573,7,2,0,0,573,574,1,0,0,0,574,575,6,18,9,0,575,52,1,0,0,0,576,578, + 8,21,0,0,577,576,1,0,0,0,578,579,1,0,0,0,579,577,1,0,0,0,579,580,1,0,0, + 0,580,581,1,0,0,0,581,582,6,19,0,0,582,54,1,0,0,0,583,584,5,47,0,0,584, + 585,5,47,0,0,585,589,1,0,0,0,586,588,8,22,0,0,587,586,1,0,0,0,588,591,1, + 0,0,0,589,587,1,0,0,0,589,590,1,0,0,0,590,593,1,0,0,0,591,589,1,0,0,0,592, + 594,5,13,0,0,593,592,1,0,0,0,593,594,1,0,0,0,594,596,1,0,0,0,595,597,5, + 10,0,0,596,595,1,0,0,0,596,597,1,0,0,0,597,598,1,0,0,0,598,599,6,20,10, + 0,599,56,1,0,0,0,600,601,5,47,0,0,601,602,5,42,0,0,602,607,1,0,0,0,603, + 606,3,57,21,0,604,606,9,0,0,0,605,603,1,0,0,0,605,604,1,0,0,0,606,609,1, + 0,0,0,607,608,1,0,0,0,607,605,1,0,0,0,608,610,1,0,0,0,609,607,1,0,0,0,610, + 611,5,42,0,0,611,612,5,47,0,0,612,613,1,0,0,0,613,614,6,21,10,0,614,58, + 1,0,0,0,615,617,7,23,0,0,616,615,1,0,0,0,617,618,1,0,0,0,618,616,1,0,0, + 0,618,619,1,0,0,0,619,620,1,0,0,0,620,621,6,22,10,0,621,60,1,0,0,0,622, + 623,5,124,0,0,623,624,1,0,0,0,624,625,6,23,11,0,625,62,1,0,0,0,626,627, + 7,24,0,0,627,64,1,0,0,0,628,629,7,25,0,0,629,66,1,0,0,0,630,631,5,92,0, + 0,631,632,7,26,0,0,632,68,1,0,0,0,633,634,8,27,0,0,634,70,1,0,0,0,635,637, + 7,3,0,0,636,638,7,28,0,0,637,636,1,0,0,0,637,638,1,0,0,0,638,640,1,0,0, + 0,639,641,3,63,24,0,640,639,1,0,0,0,641,642,1,0,0,0,642,640,1,0,0,0,642, + 643,1,0,0,0,643,72,1,0,0,0,644,645,5,64,0,0,645,74,1,0,0,0,646,647,5,96, + 0,0,647,76,1,0,0,0,648,652,8,29,0,0,649,650,5,96,0,0,650,652,5,96,0,0,651, + 648,1,0,0,0,651,649,1,0,0,0,652,78,1,0,0,0,653,654,5,95,0,0,654,80,1,0, + 0,0,655,659,3,65,25,0,656,659,3,63,24,0,657,659,3,79,32,0,658,655,1,0,0, + 0,658,656,1,0,0,0,658,657,1,0,0,0,659,82,1,0,0,0,660,665,5,34,0,0,661,664, + 3,67,26,0,662,664,3,69,27,0,663,661,1,0,0,0,663,662,1,0,0,0,664,667,1,0, + 0,0,665,663,1,0,0,0,665,666,1,0,0,0,666,668,1,0,0,0,667,665,1,0,0,0,668, + 690,5,34,0,0,669,670,5,34,0,0,670,671,5,34,0,0,671,672,5,34,0,0,672,676, + 1,0,0,0,673,675,8,22,0,0,674,673,1,0,0,0,675,678,1,0,0,0,676,677,1,0,0, + 0,676,674,1,0,0,0,677,679,1,0,0,0,678,676,1,0,0,0,679,680,5,34,0,0,680, + 681,5,34,0,0,681,682,5,34,0,0,682,684,1,0,0,0,683,685,5,34,0,0,684,683, + 1,0,0,0,684,685,1,0,0,0,685,687,1,0,0,0,686,688,5,34,0,0,687,686,1,0,0, + 0,687,688,1,0,0,0,688,690,1,0,0,0,689,660,1,0,0,0,689,669,1,0,0,0,690,84, + 1,0,0,0,691,693,3,63,24,0,692,691,1,0,0,0,693,694,1,0,0,0,694,692,1,0,0, + 0,694,695,1,0,0,0,695,86,1,0,0,0,696,698,3,63,24,0,697,696,1,0,0,0,698, + 699,1,0,0,0,699,697,1,0,0,0,699,700,1,0,0,0,700,701,1,0,0,0,701,705,3,103, + 44,0,702,704,3,63,24,0,703,702,1,0,0,0,704,707,1,0,0,0,705,703,1,0,0,0, + 705,706,1,0,0,0,706,739,1,0,0,0,707,705,1,0,0,0,708,710,3,103,44,0,709, + 711,3,63,24,0,710,709,1,0,0,0,711,712,1,0,0,0,712,710,1,0,0,0,712,713,1, + 0,0,0,713,739,1,0,0,0,714,716,3,63,24,0,715,714,1,0,0,0,716,717,1,0,0,0, + 717,715,1,0,0,0,717,718,1,0,0,0,718,726,1,0,0,0,719,723,3,103,44,0,720, + 722,3,63,24,0,721,720,1,0,0,0,722,725,1,0,0,0,723,721,1,0,0,0,723,724,1, + 0,0,0,724,727,1,0,0,0,725,723,1,0,0,0,726,719,1,0,0,0,726,727,1,0,0,0,727, + 728,1,0,0,0,728,729,3,71,28,0,729,739,1,0,0,0,730,732,3,103,44,0,731,733, + 3,63,24,0,732,731,1,0,0,0,733,734,1,0,0,0,734,732,1,0,0,0,734,735,1,0,0, + 0,735,736,1,0,0,0,736,737,3,71,28,0,737,739,1,0,0,0,738,697,1,0,0,0,738, + 708,1,0,0,0,738,715,1,0,0,0,738,730,1,0,0,0,739,88,1,0,0,0,740,741,7,30, + 0,0,741,742,7,31,0,0,742,90,1,0,0,0,743,744,7,12,0,0,744,745,7,9,0,0,745, + 746,7,0,0,0,746,92,1,0,0,0,747,748,7,12,0,0,748,749,7,2,0,0,749,750,7,4, + 0,0,750,94,1,0,0,0,751,752,5,61,0,0,752,96,1,0,0,0,753,754,5,58,0,0,754, + 755,5,58,0,0,755,98,1,0,0,0,756,757,5,44,0,0,757,100,1,0,0,0,758,759,7, + 0,0,0,759,760,7,3,0,0,760,761,7,2,0,0,761,762,7,4,0,0,762,102,1,0,0,0,763, + 764,5,46,0,0,764,104,1,0,0,0,765,766,7,15,0,0,766,767,7,12,0,0,767,768, + 7,13,0,0,768,769,7,2,0,0,769,770,7,3,0,0,770,106,1,0,0,0,771,772,7,15,0, + 0,772,773,7,1,0,0,773,774,7,6,0,0,774,775,7,2,0,0,775,776,7,5,0,0,776,108, + 1,0,0,0,777,778,7,1,0,0,778,779,7,9,0,0,779,110,1,0,0,0,780,781,7,1,0,0, + 781,782,7,2,0,0,782,112,1,0,0,0,783,784,7,13,0,0,784,785,7,12,0,0,785,786, + 7,2,0,0,786,787,7,5,0,0,787,114,1,0,0,0,788,789,7,13,0,0,789,790,7,1,0, + 0,790,791,7,18,0,0,791,792,7,3,0,0,792,116,1,0,0,0,793,794,5,40,0,0,794, + 118,1,0,0,0,795,796,7,9,0,0,796,797,7,7,0,0,797,798,7,5,0,0,798,120,1,0, + 0,0,799,800,7,9,0,0,800,801,7,20,0,0,801,802,7,13,0,0,802,803,7,13,0,0, + 803,122,1,0,0,0,804,805,7,9,0,0,805,806,7,20,0,0,806,807,7,13,0,0,807,808, + 7,13,0,0,808,809,7,2,0,0,809,124,1,0,0,0,810,811,7,7,0,0,811,812,7,6,0, + 0,812,126,1,0,0,0,813,814,5,63,0,0,814,128,1,0,0,0,815,816,7,6,0,0,816, + 817,7,13,0,0,817,818,7,1,0,0,818,819,7,18,0,0,819,820,7,3,0,0,820,130,1, + 0,0,0,821,822,5,41,0,0,822,132,1,0,0,0,823,824,7,5,0,0,824,825,7,6,0,0, + 825,826,7,20,0,0,826,827,7,3,0,0,827,134,1,0,0,0,828,829,5,61,0,0,829,830, + 5,61,0,0,830,136,1,0,0,0,831,832,5,61,0,0,832,833,5,126,0,0,833,138,1,0, + 0,0,834,835,5,33,0,0,835,836,5,61,0,0,836,140,1,0,0,0,837,838,5,60,0,0, + 838,142,1,0,0,0,839,840,5,60,0,0,840,841,5,61,0,0,841,144,1,0,0,0,842,843, + 5,62,0,0,843,146,1,0,0,0,844,845,5,62,0,0,845,846,5,61,0,0,846,148,1,0, + 0,0,847,848,5,43,0,0,848,150,1,0,0,0,849,850,5,45,0,0,850,152,1,0,0,0,851, + 852,5,42,0,0,852,154,1,0,0,0,853,854,5,47,0,0,854,156,1,0,0,0,855,856,5, + 37,0,0,856,158,1,0,0,0,857,858,7,16,0,0,858,859,7,12,0,0,859,860,7,5,0, + 0,860,861,7,4,0,0,861,862,7,10,0,0,862,160,1,0,0,0,863,864,3,45,15,0,864, + 865,1,0,0,0,865,866,6,73,12,0,866,162,1,0,0,0,867,870,3,127,56,0,868,871, + 3,65,25,0,869,871,3,79,32,0,870,868,1,0,0,0,870,869,1,0,0,0,871,875,1,0, + 0,0,872,874,3,81,33,0,873,872,1,0,0,0,874,877,1,0,0,0,875,873,1,0,0,0,875, + 876,1,0,0,0,876,885,1,0,0,0,877,875,1,0,0,0,878,880,3,127,56,0,879,881, + 3,63,24,0,880,879,1,0,0,0,881,882,1,0,0,0,882,880,1,0,0,0,882,883,1,0,0, + 0,883,885,1,0,0,0,884,867,1,0,0,0,884,878,1,0,0,0,885,164,1,0,0,0,886,887, + 5,91,0,0,887,888,1,0,0,0,888,889,6,75,0,0,889,890,6,75,0,0,890,166,1,0, + 0,0,891,892,5,93,0,0,892,893,1,0,0,0,893,894,6,76,11,0,894,895,6,76,11, + 0,895,168,1,0,0,0,896,900,3,65,25,0,897,899,3,81,33,0,898,897,1,0,0,0,899, + 902,1,0,0,0,900,898,1,0,0,0,900,901,1,0,0,0,901,913,1,0,0,0,902,900,1,0, + 0,0,903,906,3,79,32,0,904,906,3,73,29,0,905,903,1,0,0,0,905,904,1,0,0,0, + 906,908,1,0,0,0,907,909,3,81,33,0,908,907,1,0,0,0,909,910,1,0,0,0,910,908, + 1,0,0,0,910,911,1,0,0,0,911,913,1,0,0,0,912,896,1,0,0,0,912,905,1,0,0,0, + 913,170,1,0,0,0,914,916,3,75,30,0,915,917,3,77,31,0,916,915,1,0,0,0,917, + 918,1,0,0,0,918,916,1,0,0,0,918,919,1,0,0,0,919,920,1,0,0,0,920,921,3,75, + 30,0,921,172,1,0,0,0,922,923,3,171,78,0,923,174,1,0,0,0,924,925,3,55,20, + 0,925,926,1,0,0,0,926,927,6,80,10,0,927,176,1,0,0,0,928,929,3,57,21,0,929, + 930,1,0,0,0,930,931,6,81,10,0,931,178,1,0,0,0,932,933,3,59,22,0,933,934, + 1,0,0,0,934,935,6,82,10,0,935,180,1,0,0,0,936,937,3,165,75,0,937,938,1, + 0,0,0,938,939,6,83,13,0,939,940,6,83,14,0,940,182,1,0,0,0,941,942,3,61, + 23,0,942,943,1,0,0,0,943,944,6,84,15,0,944,945,6,84,11,0,945,184,1,0,0, + 0,946,947,3,59,22,0,947,948,1,0,0,0,948,949,6,85,10,0,949,186,1,0,0,0,950, + 951,3,55,20,0,951,952,1,0,0,0,952,953,6,86,10,0,953,188,1,0,0,0,954,955, + 3,57,21,0,955,956,1,0,0,0,956,957,6,87,10,0,957,190,1,0,0,0,958,959,3,61, + 23,0,959,960,1,0,0,0,960,961,6,88,15,0,961,962,6,88,11,0,962,192,1,0,0, + 0,963,964,3,165,75,0,964,965,1,0,0,0,965,966,6,89,13,0,966,194,1,0,0,0, + 967,968,3,167,76,0,968,969,1,0,0,0,969,970,6,90,16,0,970,196,1,0,0,0,971, + 972,3,337,161,0,972,973,1,0,0,0,973,974,6,91,17,0,974,198,1,0,0,0,975,976, + 3,99,42,0,976,977,1,0,0,0,977,978,6,92,18,0,978,200,1,0,0,0,979,980,3,95, + 40,0,980,981,1,0,0,0,981,982,6,93,19,0,982,202,1,0,0,0,983,984,7,16,0,0, + 984,985,7,3,0,0,985,986,7,5,0,0,986,987,7,12,0,0,987,988,7,0,0,0,988,989, + 7,12,0,0,989,990,7,5,0,0,990,991,7,12,0,0,991,204,1,0,0,0,992,996,8,32, + 0,0,993,994,5,47,0,0,994,996,8,33,0,0,995,992,1,0,0,0,995,993,1,0,0,0,996, + 206,1,0,0,0,997,999,3,205,95,0,998,997,1,0,0,0,999,1000,1,0,0,0,1000,998, + 1,0,0,0,1000,1001,1,0,0,0,1001,208,1,0,0,0,1002,1003,3,207,96,0,1003,1004, + 1,0,0,0,1004,1005,6,97,20,0,1005,210,1,0,0,0,1006,1007,3,83,34,0,1007,1008, + 1,0,0,0,1008,1009,6,98,21,0,1009,212,1,0,0,0,1010,1011,3,55,20,0,1011,1012, + 1,0,0,0,1012,1013,6,99,10,0,1013,214,1,0,0,0,1014,1015,3,57,21,0,1015,1016, + 1,0,0,0,1016,1017,6,100,10,0,1017,216,1,0,0,0,1018,1019,3,59,22,0,1019, + 1020,1,0,0,0,1020,1021,6,101,10,0,1021,218,1,0,0,0,1022,1023,3,61,23,0, + 1023,1024,1,0,0,0,1024,1025,6,102,15,0,1025,1026,6,102,11,0,1026,220,1, + 0,0,0,1027,1028,3,103,44,0,1028,1029,1,0,0,0,1029,1030,6,103,22,0,1030, + 222,1,0,0,0,1031,1032,3,99,42,0,1032,1033,1,0,0,0,1033,1034,6,104,18,0, + 1034,224,1,0,0,0,1035,1036,4,105,3,0,1036,1037,3,127,56,0,1037,1038,1,0, + 0,0,1038,1039,6,105,23,0,1039,226,1,0,0,0,1040,1041,4,106,4,0,1041,1042, + 3,163,74,0,1042,1043,1,0,0,0,1043,1044,6,106,24,0,1044,228,1,0,0,0,1045, + 1050,3,65,25,0,1046,1050,3,63,24,0,1047,1050,3,79,32,0,1048,1050,3,153, + 69,0,1049,1045,1,0,0,0,1049,1046,1,0,0,0,1049,1047,1,0,0,0,1049,1048,1, + 0,0,0,1050,230,1,0,0,0,1051,1054,3,65,25,0,1052,1054,3,153,69,0,1053,1051, + 1,0,0,0,1053,1052,1,0,0,0,1054,1058,1,0,0,0,1055,1057,3,229,107,0,1056, + 1055,1,0,0,0,1057,1060,1,0,0,0,1058,1056,1,0,0,0,1058,1059,1,0,0,0,1059, + 1071,1,0,0,0,1060,1058,1,0,0,0,1061,1064,3,79,32,0,1062,1064,3,73,29,0, + 1063,1061,1,0,0,0,1063,1062,1,0,0,0,1064,1066,1,0,0,0,1065,1067,3,229,107, + 0,1066,1065,1,0,0,0,1067,1068,1,0,0,0,1068,1066,1,0,0,0,1068,1069,1,0,0, + 0,1069,1071,1,0,0,0,1070,1053,1,0,0,0,1070,1063,1,0,0,0,1071,232,1,0,0, + 0,1072,1075,3,231,108,0,1073,1075,3,171,78,0,1074,1072,1,0,0,0,1074,1073, + 1,0,0,0,1075,1076,1,0,0,0,1076,1074,1,0,0,0,1076,1077,1,0,0,0,1077,234, + 1,0,0,0,1078,1079,3,55,20,0,1079,1080,1,0,0,0,1080,1081,6,110,10,0,1081, + 236,1,0,0,0,1082,1083,3,57,21,0,1083,1084,1,0,0,0,1084,1085,6,111,10,0, + 1085,238,1,0,0,0,1086,1087,3,59,22,0,1087,1088,1,0,0,0,1088,1089,6,112, + 10,0,1089,240,1,0,0,0,1090,1091,3,61,23,0,1091,1092,1,0,0,0,1092,1093,6, + 113,15,0,1093,1094,6,113,11,0,1094,242,1,0,0,0,1095,1096,3,95,40,0,1096, + 1097,1,0,0,0,1097,1098,6,114,19,0,1098,244,1,0,0,0,1099,1100,3,99,42,0, + 1100,1101,1,0,0,0,1101,1102,6,115,18,0,1102,246,1,0,0,0,1103,1104,3,103, + 44,0,1104,1105,1,0,0,0,1105,1106,6,116,22,0,1106,248,1,0,0,0,1107,1108, + 4,117,5,0,1108,1109,3,127,56,0,1109,1110,1,0,0,0,1110,1111,6,117,23,0,1111, + 250,1,0,0,0,1112,1113,4,118,6,0,1113,1114,3,163,74,0,1114,1115,1,0,0,0, + 1115,1116,6,118,24,0,1116,252,1,0,0,0,1117,1118,7,12,0,0,1118,1119,7,2, + 0,0,1119,254,1,0,0,0,1120,1121,3,233,109,0,1121,1122,1,0,0,0,1122,1123, + 6,120,25,0,1123,256,1,0,0,0,1124,1125,3,55,20,0,1125,1126,1,0,0,0,1126, + 1127,6,121,10,0,1127,258,1,0,0,0,1128,1129,3,57,21,0,1129,1130,1,0,0,0, + 1130,1131,6,122,10,0,1131,260,1,0,0,0,1132,1133,3,59,22,0,1133,1134,1,0, + 0,0,1134,1135,6,123,10,0,1135,262,1,0,0,0,1136,1137,3,61,23,0,1137,1138, + 1,0,0,0,1138,1139,6,124,15,0,1139,1140,6,124,11,0,1140,264,1,0,0,0,1141, + 1142,3,165,75,0,1142,1143,1,0,0,0,1143,1144,6,125,13,0,1144,1145,6,125, + 26,0,1145,266,1,0,0,0,1146,1147,7,7,0,0,1147,1148,7,9,0,0,1148,1149,1,0, + 0,0,1149,1150,6,126,27,0,1150,268,1,0,0,0,1151,1152,7,19,0,0,1152,1153, + 7,1,0,0,1153,1154,7,5,0,0,1154,1155,7,10,0,0,1155,1156,1,0,0,0,1156,1157, + 6,127,27,0,1157,270,1,0,0,0,1158,1159,8,34,0,0,1159,272,1,0,0,0,1160,1162, + 3,271,128,0,1161,1160,1,0,0,0,1162,1163,1,0,0,0,1163,1161,1,0,0,0,1163, + 1164,1,0,0,0,1164,1165,1,0,0,0,1165,1166,3,337,161,0,1166,1168,1,0,0,0, + 1167,1161,1,0,0,0,1167,1168,1,0,0,0,1168,1170,1,0,0,0,1169,1171,3,271,128, + 0,1170,1169,1,0,0,0,1171,1172,1,0,0,0,1172,1170,1,0,0,0,1172,1173,1,0,0, + 0,1173,274,1,0,0,0,1174,1175,3,273,129,0,1175,1176,1,0,0,0,1176,1177,6, + 130,28,0,1177,276,1,0,0,0,1178,1179,3,55,20,0,1179,1180,1,0,0,0,1180,1181, + 6,131,10,0,1181,278,1,0,0,0,1182,1183,3,57,21,0,1183,1184,1,0,0,0,1184, + 1185,6,132,10,0,1185,280,1,0,0,0,1186,1187,3,59,22,0,1187,1188,1,0,0,0, + 1188,1189,6,133,10,0,1189,282,1,0,0,0,1190,1191,3,61,23,0,1191,1192,1,0, + 0,0,1192,1193,6,134,15,0,1193,1194,6,134,11,0,1194,1195,6,134,11,0,1195, + 284,1,0,0,0,1196,1197,3,95,40,0,1197,1198,1,0,0,0,1198,1199,6,135,19,0, + 1199,286,1,0,0,0,1200,1201,3,99,42,0,1201,1202,1,0,0,0,1202,1203,6,136, + 18,0,1203,288,1,0,0,0,1204,1205,3,103,44,0,1205,1206,1,0,0,0,1206,1207, + 6,137,22,0,1207,290,1,0,0,0,1208,1209,3,269,127,0,1209,1210,1,0,0,0,1210, + 1211,6,138,29,0,1211,292,1,0,0,0,1212,1213,3,233,109,0,1213,1214,1,0,0, + 0,1214,1215,6,139,25,0,1215,294,1,0,0,0,1216,1217,3,173,79,0,1217,1218, + 1,0,0,0,1218,1219,6,140,30,0,1219,296,1,0,0,0,1220,1221,4,141,7,0,1221, + 1222,3,127,56,0,1222,1223,1,0,0,0,1223,1224,6,141,23,0,1224,298,1,0,0,0, + 1225,1226,4,142,8,0,1226,1227,3,163,74,0,1227,1228,1,0,0,0,1228,1229,6, + 142,24,0,1229,300,1,0,0,0,1230,1231,3,55,20,0,1231,1232,1,0,0,0,1232,1233, + 6,143,10,0,1233,302,1,0,0,0,1234,1235,3,57,21,0,1235,1236,1,0,0,0,1236, + 1237,6,144,10,0,1237,304,1,0,0,0,1238,1239,3,59,22,0,1239,1240,1,0,0,0, + 1240,1241,6,145,10,0,1241,306,1,0,0,0,1242,1243,3,61,23,0,1243,1244,1,0, + 0,0,1244,1245,6,146,15,0,1245,1246,6,146,11,0,1246,308,1,0,0,0,1247,1248, + 3,103,44,0,1248,1249,1,0,0,0,1249,1250,6,147,22,0,1250,310,1,0,0,0,1251, + 1252,4,148,9,0,1252,1253,3,127,56,0,1253,1254,1,0,0,0,1254,1255,6,148,23, + 0,1255,312,1,0,0,0,1256,1257,4,149,10,0,1257,1258,3,163,74,0,1258,1259, + 1,0,0,0,1259,1260,6,149,24,0,1260,314,1,0,0,0,1261,1262,3,173,79,0,1262, + 1263,1,0,0,0,1263,1264,6,150,30,0,1264,316,1,0,0,0,1265,1266,3,169,77,0, + 1266,1267,1,0,0,0,1267,1268,6,151,31,0,1268,318,1,0,0,0,1269,1270,3,55, + 20,0,1270,1271,1,0,0,0,1271,1272,6,152,10,0,1272,320,1,0,0,0,1273,1274, + 3,57,21,0,1274,1275,1,0,0,0,1275,1276,6,153,10,0,1276,322,1,0,0,0,1277, + 1278,3,59,22,0,1278,1279,1,0,0,0,1279,1280,6,154,10,0,1280,324,1,0,0,0, + 1281,1282,3,61,23,0,1282,1283,1,0,0,0,1283,1284,6,155,15,0,1284,1285,6, + 155,11,0,1285,326,1,0,0,0,1286,1287,7,1,0,0,1287,1288,7,9,0,0,1288,1289, + 7,15,0,0,1289,1290,7,7,0,0,1290,328,1,0,0,0,1291,1292,3,55,20,0,1292,1293, + 1,0,0,0,1293,1294,6,157,10,0,1294,330,1,0,0,0,1295,1296,3,57,21,0,1296, + 1297,1,0,0,0,1297,1298,6,158,10,0,1298,332,1,0,0,0,1299,1300,3,59,22,0, + 1300,1301,1,0,0,0,1301,1302,6,159,10,0,1302,334,1,0,0,0,1303,1304,3,167, + 76,0,1304,1305,1,0,0,0,1305,1306,6,160,16,0,1306,1307,6,160,11,0,1307,336, + 1,0,0,0,1308,1309,5,58,0,0,1309,338,1,0,0,0,1310,1316,3,73,29,0,1311,1316, + 3,63,24,0,1312,1316,3,103,44,0,1313,1316,3,65,25,0,1314,1316,3,79,32,0, + 1315,1310,1,0,0,0,1315,1311,1,0,0,0,1315,1312,1,0,0,0,1315,1313,1,0,0,0, + 1315,1314,1,0,0,0,1316,1317,1,0,0,0,1317,1315,1,0,0,0,1317,1318,1,0,0,0, + 1318,340,1,0,0,0,1319,1320,3,55,20,0,1320,1321,1,0,0,0,1321,1322,6,163, + 10,0,1322,342,1,0,0,0,1323,1324,3,57,21,0,1324,1325,1,0,0,0,1325,1326,6, + 164,10,0,1326,344,1,0,0,0,1327,1328,3,59,22,0,1328,1329,1,0,0,0,1329,1330, + 6,165,10,0,1330,346,1,0,0,0,1331,1332,3,61,23,0,1332,1333,1,0,0,0,1333, + 1334,6,166,15,0,1334,1335,6,166,11,0,1335,348,1,0,0,0,1336,1337,3,337,161, + 0,1337,1338,1,0,0,0,1338,1339,6,167,17,0,1339,350,1,0,0,0,1340,1341,3,99, + 42,0,1341,1342,1,0,0,0,1342,1343,6,168,18,0,1343,352,1,0,0,0,1344,1345, + 3,103,44,0,1345,1346,1,0,0,0,1346,1347,6,169,22,0,1347,354,1,0,0,0,1348, + 1349,3,267,126,0,1349,1350,1,0,0,0,1350,1351,6,170,32,0,1351,1352,6,170, + 33,0,1352,356,1,0,0,0,1353,1354,3,207,96,0,1354,1355,1,0,0,0,1355,1356, + 6,171,20,0,1356,358,1,0,0,0,1357,1358,3,83,34,0,1358,1359,1,0,0,0,1359, + 1360,6,172,21,0,1360,360,1,0,0,0,1361,1362,3,55,20,0,1362,1363,1,0,0,0, + 1363,1364,6,173,10,0,1364,362,1,0,0,0,1365,1366,3,57,21,0,1366,1367,1,0, + 0,0,1367,1368,6,174,10,0,1368,364,1,0,0,0,1369,1370,3,59,22,0,1370,1371, + 1,0,0,0,1371,1372,6,175,10,0,1372,366,1,0,0,0,1373,1374,3,61,23,0,1374, + 1375,1,0,0,0,1375,1376,6,176,15,0,1376,1377,6,176,11,0,1377,1378,6,176, + 11,0,1378,368,1,0,0,0,1379,1380,3,99,42,0,1380,1381,1,0,0,0,1381,1382,6, + 177,18,0,1382,370,1,0,0,0,1383,1384,3,103,44,0,1384,1385,1,0,0,0,1385,1386, + 6,178,22,0,1386,372,1,0,0,0,1387,1388,3,233,109,0,1388,1389,1,0,0,0,1389, + 1390,6,179,25,0,1390,374,1,0,0,0,1391,1392,3,55,20,0,1392,1393,1,0,0,0, + 1393,1394,6,180,10,0,1394,376,1,0,0,0,1395,1396,3,57,21,0,1396,1397,1,0, + 0,0,1397,1398,6,181,10,0,1398,378,1,0,0,0,1399,1400,3,59,22,0,1400,1401, + 1,0,0,0,1401,1402,6,182,10,0,1402,380,1,0,0,0,1403,1404,3,61,23,0,1404, + 1405,1,0,0,0,1405,1406,6,183,15,0,1406,1407,6,183,11,0,1407,382,1,0,0,0, + 1408,1409,3,207,96,0,1409,1410,1,0,0,0,1410,1411,6,184,20,0,1411,1412,6, + 184,11,0,1412,1413,6,184,34,0,1413,384,1,0,0,0,1414,1415,3,83,34,0,1415, + 1416,1,0,0,0,1416,1417,6,185,21,0,1417,1418,6,185,11,0,1418,1419,6,185, + 34,0,1419,386,1,0,0,0,1420,1421,3,55,20,0,1421,1422,1,0,0,0,1422,1423,6, + 186,10,0,1423,388,1,0,0,0,1424,1425,3,57,21,0,1425,1426,1,0,0,0,1426,1427, + 6,187,10,0,1427,390,1,0,0,0,1428,1429,3,59,22,0,1429,1430,1,0,0,0,1430, + 1431,6,188,10,0,1431,392,1,0,0,0,1432,1433,3,337,161,0,1433,1434,1,0,0, + 0,1434,1435,6,189,17,0,1435,1436,6,189,11,0,1436,1437,6,189,9,0,1437,394, + 1,0,0,0,1438,1439,3,99,42,0,1439,1440,1,0,0,0,1440,1441,6,190,18,0,1441, + 1442,6,190,11,0,1442,1443,6,190,9,0,1443,396,1,0,0,0,1444,1445,3,55,20, + 0,1445,1446,1,0,0,0,1446,1447,6,191,10,0,1447,398,1,0,0,0,1448,1449,3,57, + 21,0,1449,1450,1,0,0,0,1450,1451,6,192,10,0,1451,400,1,0,0,0,1452,1453, + 3,59,22,0,1453,1454,1,0,0,0,1454,1455,6,193,10,0,1455,402,1,0,0,0,1456, + 1457,3,173,79,0,1457,1458,1,0,0,0,1458,1459,6,194,11,0,1459,1460,6,194, + 0,0,1460,1461,6,194,30,0,1461,404,1,0,0,0,1462,1463,3,169,77,0,1463,1464, + 1,0,0,0,1464,1465,6,195,11,0,1465,1466,6,195,0,0,1466,1467,6,195,31,0,1467, + 406,1,0,0,0,1468,1469,3,89,37,0,1469,1470,1,0,0,0,1470,1471,6,196,11,0, + 1471,1472,6,196,0,0,1472,1473,6,196,35,0,1473,408,1,0,0,0,1474,1475,3,61, + 23,0,1475,1476,1,0,0,0,1476,1477,6,197,15,0,1477,1478,6,197,11,0,1478,410, + 1,0,0,0,65,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,579,589,593,596,605,607,618, + 637,642,651,658,663,665,676,684,687,689,694,699,705,712,717,723,726,734, + 738,870,875,882,884,900,905,910,912,918,995,1000,1049,1053,1058,1063,1068, + 1070,1074,1076,1163,1167,1172,1315,1317,36,5,1,0,5,4,0,5,6,0,5,2,0,5,3, + 0,5,8,0,5,5,0,5,9,0,5,11,0,5,13,0,0,1,0,4,0,0,7,16,0,7,65,0,5,0,0,7,24, + 0,7,66,0,7,104,0,7,33,0,7,31,0,7,76,0,7,25,0,7,35,0,7,47,0,7,64,0,7,80, + 0,5,10,0,5,7,0,7,90,0,7,89,0,7,68,0,7,67,0,7,88,0,5,12,0,5,14,0,7,28,0]; private static __ATN: ATN; public static get _ATN(): ATN { diff --git a/packages/kbn-esql-ast/src/antlr/esql_parser.g4 b/packages/kbn-esql-ast/src/antlr/esql_parser.g4 index 9d52d84dcc587..261b4f712b5b3 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_parser.g4 +++ b/packages/kbn-esql-ast/src/antlr/esql_parser.g4 @@ -79,7 +79,7 @@ regexBooleanExpression ; matchBooleanExpression - : valueExpression DEV_MATCH queryString=string + : valueExpression MATCH queryString=string ; valueExpression @@ -103,7 +103,13 @@ primaryExpression ; functionExpression - : identifierOrParameter LP (ASTERISK | (booleanExpression (COMMA booleanExpression)*))? RP + : functionName LP (ASTERISK | (booleanExpression (COMMA booleanExpression)*))? RP + ; + +functionName + // Additional function identifiers that are already a reserved word in the language + : MATCH + | identifierOrParameter ; dataType @@ -119,8 +125,7 @@ fields ; field - : booleanExpression - | qualifiedName ASSIGN booleanExpression + : (qualifiedName ASSIGN)? booleanExpression ; fromCommand @@ -128,8 +133,7 @@ fromCommand ; indexPattern - : clusterString COLON indexString - | indexString + : (clusterString COLON)? indexString ; clusterString @@ -155,7 +159,7 @@ deprecated_metadata ; metricsCommand - : DEV_METRICS indexPattern (COMMA indexPattern)* aggregates=fields? (BY grouping=fields)? + : DEV_METRICS indexPattern (COMMA indexPattern)* aggregates=aggFields? (BY grouping=fields)? ; evalCommand @@ -163,7 +167,15 @@ evalCommand ; statsCommand - : STATS stats=fields? (BY grouping=fields)? + : STATS stats=aggFields? (BY grouping=fields)? + ; + +aggFields + : aggField (COMMA aggField)* + ; + +aggField + : field (WHERE booleanExpression)? ; qualifiedName @@ -185,7 +197,7 @@ identifier identifierPattern : ID_PATTERN - | parameter + | {this.isDevVersion()}? parameter ; constant @@ -208,7 +220,7 @@ parameter identifierOrParameter : identifier - | parameter + | {this.isDevVersion()}? parameter ; limitCommand @@ -312,5 +324,5 @@ lookupCommand ; inlinestatsCommand - : DEV_INLINESTATS stats=fields (BY grouping=fields)? + : DEV_INLINESTATS stats=aggFields (BY grouping=fields)? ; \ No newline at end of file diff --git a/packages/kbn-esql-ast/src/antlr/esql_parser.interp b/packages/kbn-esql-ast/src/antlr/esql_parser.interp index eb3c70385d628..b52d842e79fb2 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_parser.interp +++ b/packages/kbn-esql-ast/src/antlr/esql_parser.interp @@ -23,7 +23,6 @@ null null null null -null '|' null null @@ -63,6 +62,7 @@ null '*' '/' '%' +'match' null null ']' @@ -141,7 +141,6 @@ STATS WHERE DEV_INLINESTATS DEV_LOOKUP -DEV_MATCH DEV_METRICS UNKNOWN_CMD LINE_COMMENT @@ -186,6 +185,7 @@ MINUS ASTERISK SLASH PERCENT +MATCH NAMED_OR_POSITIONAL_PARAM OPENING_BRACKET CLOSING_BRACKET @@ -257,6 +257,7 @@ valueExpression operatorExpression primaryExpression functionExpression +functionName dataType rowCommand fields @@ -271,6 +272,8 @@ deprecated_metadata metricsCommand evalCommand statsCommand +aggFields +aggField qualifiedName qualifiedNamePattern qualifiedNamePatterns @@ -307,4 +310,4 @@ inlinestatsCommand atn: -[4, 1, 120, 580, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 128, 8, 1, 10, 1, 12, 1, 131, 9, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 139, 8, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 157, 8, 3, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 169, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 176, 8, 5, 10, 5, 12, 5, 179, 9, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 186, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 192, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 200, 8, 5, 10, 5, 12, 5, 203, 9, 5, 1, 6, 1, 6, 3, 6, 207, 8, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 3, 6, 214, 8, 6, 1, 6, 1, 6, 1, 6, 3, 6, 219, 8, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 230, 8, 8, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 236, 8, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 5, 9, 244, 8, 9, 10, 9, 12, 9, 247, 9, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 3, 10, 257, 8, 10, 1, 10, 1, 10, 1, 10, 5, 10, 262, 8, 10, 10, 10, 12, 10, 265, 9, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 5, 11, 273, 8, 11, 10, 11, 12, 11, 276, 9, 11, 3, 11, 278, 8, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 5, 14, 290, 8, 14, 10, 14, 12, 14, 293, 9, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 3, 15, 300, 8, 15, 1, 16, 1, 16, 1, 16, 1, 16, 5, 16, 306, 8, 16, 10, 16, 12, 16, 309, 9, 16, 1, 16, 3, 16, 312, 8, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 3, 17, 319, 8, 17, 1, 18, 1, 18, 1, 19, 1, 19, 1, 20, 1, 20, 3, 20, 327, 8, 20, 1, 21, 1, 21, 1, 21, 1, 21, 5, 21, 333, 8, 21, 10, 21, 12, 21, 336, 9, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 23, 5, 23, 346, 8, 23, 10, 23, 12, 23, 349, 9, 23, 1, 23, 3, 23, 352, 8, 23, 1, 23, 1, 23, 3, 23, 356, 8, 23, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 3, 25, 363, 8, 25, 1, 25, 1, 25, 3, 25, 367, 8, 25, 1, 26, 1, 26, 1, 26, 5, 26, 372, 8, 26, 10, 26, 12, 26, 375, 9, 26, 1, 27, 1, 27, 1, 27, 5, 27, 380, 8, 27, 10, 27, 12, 27, 383, 9, 27, 1, 28, 1, 28, 1, 28, 5, 28, 388, 8, 28, 10, 28, 12, 28, 391, 9, 28, 1, 29, 1, 29, 1, 30, 1, 30, 3, 30, 397, 8, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 5, 31, 412, 8, 31, 10, 31, 12, 31, 415, 9, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 5, 31, 423, 8, 31, 10, 31, 12, 31, 426, 9, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 5, 31, 434, 8, 31, 10, 31, 12, 31, 437, 9, 31, 1, 31, 1, 31, 3, 31, 441, 8, 31, 1, 32, 1, 32, 3, 32, 445, 8, 32, 1, 33, 1, 33, 3, 33, 449, 8, 33, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 35, 5, 35, 458, 8, 35, 10, 35, 12, 35, 461, 9, 35, 1, 36, 1, 36, 3, 36, 465, 8, 36, 1, 36, 1, 36, 3, 36, 469, 8, 36, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 39, 5, 39, 481, 8, 39, 10, 39, 12, 39, 484, 9, 39, 1, 40, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 41, 3, 41, 494, 8, 41, 1, 42, 1, 42, 1, 42, 1, 42, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 5, 44, 506, 8, 44, 10, 44, 12, 44, 509, 9, 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 47, 1, 47, 3, 47, 519, 8, 47, 1, 48, 3, 48, 522, 8, 48, 1, 48, 1, 48, 1, 49, 3, 49, 527, 8, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 549, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 5, 55, 555, 8, 55, 10, 55, 12, 55, 558, 9, 55, 3, 55, 560, 8, 55, 1, 56, 1, 56, 1, 56, 3, 56, 565, 8, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 58, 3, 58, 578, 8, 58, 1, 58, 0, 4, 2, 10, 18, 20, 59, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 0, 8, 1, 0, 59, 60, 1, 0, 61, 63, 2, 0, 26, 26, 76, 76, 1, 0, 67, 68, 2, 0, 31, 31, 35, 35, 2, 0, 38, 38, 41, 41, 2, 0, 37, 37, 51, 51, 2, 0, 52, 52, 54, 58, 606, 0, 118, 1, 0, 0, 0, 2, 121, 1, 0, 0, 0, 4, 138, 1, 0, 0, 0, 6, 156, 1, 0, 0, 0, 8, 158, 1, 0, 0, 0, 10, 191, 1, 0, 0, 0, 12, 218, 1, 0, 0, 0, 14, 220, 1, 0, 0, 0, 16, 229, 1, 0, 0, 0, 18, 235, 1, 0, 0, 0, 20, 256, 1, 0, 0, 0, 22, 266, 1, 0, 0, 0, 24, 281, 1, 0, 0, 0, 26, 283, 1, 0, 0, 0, 28, 286, 1, 0, 0, 0, 30, 299, 1, 0, 0, 0, 32, 301, 1, 0, 0, 0, 34, 318, 1, 0, 0, 0, 36, 320, 1, 0, 0, 0, 38, 322, 1, 0, 0, 0, 40, 326, 1, 0, 0, 0, 42, 328, 1, 0, 0, 0, 44, 337, 1, 0, 0, 0, 46, 341, 1, 0, 0, 0, 48, 357, 1, 0, 0, 0, 50, 360, 1, 0, 0, 0, 52, 368, 1, 0, 0, 0, 54, 376, 1, 0, 0, 0, 56, 384, 1, 0, 0, 0, 58, 392, 1, 0, 0, 0, 60, 396, 1, 0, 0, 0, 62, 440, 1, 0, 0, 0, 64, 444, 1, 0, 0, 0, 66, 448, 1, 0, 0, 0, 68, 450, 1, 0, 0, 0, 70, 453, 1, 0, 0, 0, 72, 462, 1, 0, 0, 0, 74, 470, 1, 0, 0, 0, 76, 473, 1, 0, 0, 0, 78, 476, 1, 0, 0, 0, 80, 485, 1, 0, 0, 0, 82, 489, 1, 0, 0, 0, 84, 495, 1, 0, 0, 0, 86, 499, 1, 0, 0, 0, 88, 502, 1, 0, 0, 0, 90, 510, 1, 0, 0, 0, 92, 514, 1, 0, 0, 0, 94, 518, 1, 0, 0, 0, 96, 521, 1, 0, 0, 0, 98, 526, 1, 0, 0, 0, 100, 530, 1, 0, 0, 0, 102, 532, 1, 0, 0, 0, 104, 534, 1, 0, 0, 0, 106, 537, 1, 0, 0, 0, 108, 541, 1, 0, 0, 0, 110, 544, 1, 0, 0, 0, 112, 564, 1, 0, 0, 0, 114, 568, 1, 0, 0, 0, 116, 573, 1, 0, 0, 0, 118, 119, 3, 2, 1, 0, 119, 120, 5, 0, 0, 1, 120, 1, 1, 0, 0, 0, 121, 122, 6, 1, -1, 0, 122, 123, 3, 4, 2, 0, 123, 129, 1, 0, 0, 0, 124, 125, 10, 1, 0, 0, 125, 126, 5, 25, 0, 0, 126, 128, 3, 6, 3, 0, 127, 124, 1, 0, 0, 0, 128, 131, 1, 0, 0, 0, 129, 127, 1, 0, 0, 0, 129, 130, 1, 0, 0, 0, 130, 3, 1, 0, 0, 0, 131, 129, 1, 0, 0, 0, 132, 139, 3, 104, 52, 0, 133, 139, 3, 32, 16, 0, 134, 139, 3, 26, 13, 0, 135, 139, 3, 108, 54, 0, 136, 137, 4, 2, 1, 0, 137, 139, 3, 46, 23, 0, 138, 132, 1, 0, 0, 0, 138, 133, 1, 0, 0, 0, 138, 134, 1, 0, 0, 0, 138, 135, 1, 0, 0, 0, 138, 136, 1, 0, 0, 0, 139, 5, 1, 0, 0, 0, 140, 157, 3, 48, 24, 0, 141, 157, 3, 8, 4, 0, 142, 157, 3, 74, 37, 0, 143, 157, 3, 68, 34, 0, 144, 157, 3, 50, 25, 0, 145, 157, 3, 70, 35, 0, 146, 157, 3, 76, 38, 0, 147, 157, 3, 78, 39, 0, 148, 157, 3, 82, 41, 0, 149, 157, 3, 84, 42, 0, 150, 157, 3, 110, 55, 0, 151, 157, 3, 86, 43, 0, 152, 153, 4, 3, 2, 0, 153, 157, 3, 116, 58, 0, 154, 155, 4, 3, 3, 0, 155, 157, 3, 114, 57, 0, 156, 140, 1, 0, 0, 0, 156, 141, 1, 0, 0, 0, 156, 142, 1, 0, 0, 0, 156, 143, 1, 0, 0, 0, 156, 144, 1, 0, 0, 0, 156, 145, 1, 0, 0, 0, 156, 146, 1, 0, 0, 0, 156, 147, 1, 0, 0, 0, 156, 148, 1, 0, 0, 0, 156, 149, 1, 0, 0, 0, 156, 150, 1, 0, 0, 0, 156, 151, 1, 0, 0, 0, 156, 152, 1, 0, 0, 0, 156, 154, 1, 0, 0, 0, 157, 7, 1, 0, 0, 0, 158, 159, 5, 16, 0, 0, 159, 160, 3, 10, 5, 0, 160, 9, 1, 0, 0, 0, 161, 162, 6, 5, -1, 0, 162, 163, 5, 44, 0, 0, 163, 192, 3, 10, 5, 8, 164, 192, 3, 16, 8, 0, 165, 192, 3, 12, 6, 0, 166, 168, 3, 16, 8, 0, 167, 169, 5, 44, 0, 0, 168, 167, 1, 0, 0, 0, 168, 169, 1, 0, 0, 0, 169, 170, 1, 0, 0, 0, 170, 171, 5, 39, 0, 0, 171, 172, 5, 43, 0, 0, 172, 177, 3, 16, 8, 0, 173, 174, 5, 34, 0, 0, 174, 176, 3, 16, 8, 0, 175, 173, 1, 0, 0, 0, 176, 179, 1, 0, 0, 0, 177, 175, 1, 0, 0, 0, 177, 178, 1, 0, 0, 0, 178, 180, 1, 0, 0, 0, 179, 177, 1, 0, 0, 0, 180, 181, 5, 50, 0, 0, 181, 192, 1, 0, 0, 0, 182, 183, 3, 16, 8, 0, 183, 185, 5, 40, 0, 0, 184, 186, 5, 44, 0, 0, 185, 184, 1, 0, 0, 0, 185, 186, 1, 0, 0, 0, 186, 187, 1, 0, 0, 0, 187, 188, 5, 45, 0, 0, 188, 192, 1, 0, 0, 0, 189, 190, 4, 5, 4, 0, 190, 192, 3, 14, 7, 0, 191, 161, 1, 0, 0, 0, 191, 164, 1, 0, 0, 0, 191, 165, 1, 0, 0, 0, 191, 166, 1, 0, 0, 0, 191, 182, 1, 0, 0, 0, 191, 189, 1, 0, 0, 0, 192, 201, 1, 0, 0, 0, 193, 194, 10, 5, 0, 0, 194, 195, 5, 30, 0, 0, 195, 200, 3, 10, 5, 6, 196, 197, 10, 4, 0, 0, 197, 198, 5, 47, 0, 0, 198, 200, 3, 10, 5, 5, 199, 193, 1, 0, 0, 0, 199, 196, 1, 0, 0, 0, 200, 203, 1, 0, 0, 0, 201, 199, 1, 0, 0, 0, 201, 202, 1, 0, 0, 0, 202, 11, 1, 0, 0, 0, 203, 201, 1, 0, 0, 0, 204, 206, 3, 16, 8, 0, 205, 207, 5, 44, 0, 0, 206, 205, 1, 0, 0, 0, 206, 207, 1, 0, 0, 0, 207, 208, 1, 0, 0, 0, 208, 209, 5, 42, 0, 0, 209, 210, 3, 100, 50, 0, 210, 219, 1, 0, 0, 0, 211, 213, 3, 16, 8, 0, 212, 214, 5, 44, 0, 0, 213, 212, 1, 0, 0, 0, 213, 214, 1, 0, 0, 0, 214, 215, 1, 0, 0, 0, 215, 216, 5, 49, 0, 0, 216, 217, 3, 100, 50, 0, 217, 219, 1, 0, 0, 0, 218, 204, 1, 0, 0, 0, 218, 211, 1, 0, 0, 0, 219, 13, 1, 0, 0, 0, 220, 221, 3, 16, 8, 0, 221, 222, 5, 19, 0, 0, 222, 223, 3, 100, 50, 0, 223, 15, 1, 0, 0, 0, 224, 230, 3, 18, 9, 0, 225, 226, 3, 18, 9, 0, 226, 227, 3, 102, 51, 0, 227, 228, 3, 18, 9, 0, 228, 230, 1, 0, 0, 0, 229, 224, 1, 0, 0, 0, 229, 225, 1, 0, 0, 0, 230, 17, 1, 0, 0, 0, 231, 232, 6, 9, -1, 0, 232, 236, 3, 20, 10, 0, 233, 234, 7, 0, 0, 0, 234, 236, 3, 18, 9, 3, 235, 231, 1, 0, 0, 0, 235, 233, 1, 0, 0, 0, 236, 245, 1, 0, 0, 0, 237, 238, 10, 2, 0, 0, 238, 239, 7, 1, 0, 0, 239, 244, 3, 18, 9, 3, 240, 241, 10, 1, 0, 0, 241, 242, 7, 0, 0, 0, 242, 244, 3, 18, 9, 2, 243, 237, 1, 0, 0, 0, 243, 240, 1, 0, 0, 0, 244, 247, 1, 0, 0, 0, 245, 243, 1, 0, 0, 0, 245, 246, 1, 0, 0, 0, 246, 19, 1, 0, 0, 0, 247, 245, 1, 0, 0, 0, 248, 249, 6, 10, -1, 0, 249, 257, 3, 62, 31, 0, 250, 257, 3, 52, 26, 0, 251, 257, 3, 22, 11, 0, 252, 253, 5, 43, 0, 0, 253, 254, 3, 10, 5, 0, 254, 255, 5, 50, 0, 0, 255, 257, 1, 0, 0, 0, 256, 248, 1, 0, 0, 0, 256, 250, 1, 0, 0, 0, 256, 251, 1, 0, 0, 0, 256, 252, 1, 0, 0, 0, 257, 263, 1, 0, 0, 0, 258, 259, 10, 1, 0, 0, 259, 260, 5, 33, 0, 0, 260, 262, 3, 24, 12, 0, 261, 258, 1, 0, 0, 0, 262, 265, 1, 0, 0, 0, 263, 261, 1, 0, 0, 0, 263, 264, 1, 0, 0, 0, 264, 21, 1, 0, 0, 0, 265, 263, 1, 0, 0, 0, 266, 267, 3, 66, 33, 0, 267, 277, 5, 43, 0, 0, 268, 278, 5, 61, 0, 0, 269, 274, 3, 10, 5, 0, 270, 271, 5, 34, 0, 0, 271, 273, 3, 10, 5, 0, 272, 270, 1, 0, 0, 0, 273, 276, 1, 0, 0, 0, 274, 272, 1, 0, 0, 0, 274, 275, 1, 0, 0, 0, 275, 278, 1, 0, 0, 0, 276, 274, 1, 0, 0, 0, 277, 268, 1, 0, 0, 0, 277, 269, 1, 0, 0, 0, 277, 278, 1, 0, 0, 0, 278, 279, 1, 0, 0, 0, 279, 280, 5, 50, 0, 0, 280, 23, 1, 0, 0, 0, 281, 282, 3, 58, 29, 0, 282, 25, 1, 0, 0, 0, 283, 284, 5, 12, 0, 0, 284, 285, 3, 28, 14, 0, 285, 27, 1, 0, 0, 0, 286, 291, 3, 30, 15, 0, 287, 288, 5, 34, 0, 0, 288, 290, 3, 30, 15, 0, 289, 287, 1, 0, 0, 0, 290, 293, 1, 0, 0, 0, 291, 289, 1, 0, 0, 0, 291, 292, 1, 0, 0, 0, 292, 29, 1, 0, 0, 0, 293, 291, 1, 0, 0, 0, 294, 300, 3, 10, 5, 0, 295, 296, 3, 52, 26, 0, 296, 297, 5, 32, 0, 0, 297, 298, 3, 10, 5, 0, 298, 300, 1, 0, 0, 0, 299, 294, 1, 0, 0, 0, 299, 295, 1, 0, 0, 0, 300, 31, 1, 0, 0, 0, 301, 302, 5, 6, 0, 0, 302, 307, 3, 34, 17, 0, 303, 304, 5, 34, 0, 0, 304, 306, 3, 34, 17, 0, 305, 303, 1, 0, 0, 0, 306, 309, 1, 0, 0, 0, 307, 305, 1, 0, 0, 0, 307, 308, 1, 0, 0, 0, 308, 311, 1, 0, 0, 0, 309, 307, 1, 0, 0, 0, 310, 312, 3, 40, 20, 0, 311, 310, 1, 0, 0, 0, 311, 312, 1, 0, 0, 0, 312, 33, 1, 0, 0, 0, 313, 314, 3, 36, 18, 0, 314, 315, 5, 104, 0, 0, 315, 316, 3, 38, 19, 0, 316, 319, 1, 0, 0, 0, 317, 319, 3, 38, 19, 0, 318, 313, 1, 0, 0, 0, 318, 317, 1, 0, 0, 0, 319, 35, 1, 0, 0, 0, 320, 321, 5, 76, 0, 0, 321, 37, 1, 0, 0, 0, 322, 323, 7, 2, 0, 0, 323, 39, 1, 0, 0, 0, 324, 327, 3, 42, 21, 0, 325, 327, 3, 44, 22, 0, 326, 324, 1, 0, 0, 0, 326, 325, 1, 0, 0, 0, 327, 41, 1, 0, 0, 0, 328, 329, 5, 75, 0, 0, 329, 334, 5, 76, 0, 0, 330, 331, 5, 34, 0, 0, 331, 333, 5, 76, 0, 0, 332, 330, 1, 0, 0, 0, 333, 336, 1, 0, 0, 0, 334, 332, 1, 0, 0, 0, 334, 335, 1, 0, 0, 0, 335, 43, 1, 0, 0, 0, 336, 334, 1, 0, 0, 0, 337, 338, 5, 65, 0, 0, 338, 339, 3, 42, 21, 0, 339, 340, 5, 66, 0, 0, 340, 45, 1, 0, 0, 0, 341, 342, 5, 20, 0, 0, 342, 347, 3, 34, 17, 0, 343, 344, 5, 34, 0, 0, 344, 346, 3, 34, 17, 0, 345, 343, 1, 0, 0, 0, 346, 349, 1, 0, 0, 0, 347, 345, 1, 0, 0, 0, 347, 348, 1, 0, 0, 0, 348, 351, 1, 0, 0, 0, 349, 347, 1, 0, 0, 0, 350, 352, 3, 28, 14, 0, 351, 350, 1, 0, 0, 0, 351, 352, 1, 0, 0, 0, 352, 355, 1, 0, 0, 0, 353, 354, 5, 29, 0, 0, 354, 356, 3, 28, 14, 0, 355, 353, 1, 0, 0, 0, 355, 356, 1, 0, 0, 0, 356, 47, 1, 0, 0, 0, 357, 358, 5, 4, 0, 0, 358, 359, 3, 28, 14, 0, 359, 49, 1, 0, 0, 0, 360, 362, 5, 15, 0, 0, 361, 363, 3, 28, 14, 0, 362, 361, 1, 0, 0, 0, 362, 363, 1, 0, 0, 0, 363, 366, 1, 0, 0, 0, 364, 365, 5, 29, 0, 0, 365, 367, 3, 28, 14, 0, 366, 364, 1, 0, 0, 0, 366, 367, 1, 0, 0, 0, 367, 51, 1, 0, 0, 0, 368, 373, 3, 66, 33, 0, 369, 370, 5, 36, 0, 0, 370, 372, 3, 66, 33, 0, 371, 369, 1, 0, 0, 0, 372, 375, 1, 0, 0, 0, 373, 371, 1, 0, 0, 0, 373, 374, 1, 0, 0, 0, 374, 53, 1, 0, 0, 0, 375, 373, 1, 0, 0, 0, 376, 381, 3, 60, 30, 0, 377, 378, 5, 36, 0, 0, 378, 380, 3, 60, 30, 0, 379, 377, 1, 0, 0, 0, 380, 383, 1, 0, 0, 0, 381, 379, 1, 0, 0, 0, 381, 382, 1, 0, 0, 0, 382, 55, 1, 0, 0, 0, 383, 381, 1, 0, 0, 0, 384, 389, 3, 54, 27, 0, 385, 386, 5, 34, 0, 0, 386, 388, 3, 54, 27, 0, 387, 385, 1, 0, 0, 0, 388, 391, 1, 0, 0, 0, 389, 387, 1, 0, 0, 0, 389, 390, 1, 0, 0, 0, 390, 57, 1, 0, 0, 0, 391, 389, 1, 0, 0, 0, 392, 393, 7, 3, 0, 0, 393, 59, 1, 0, 0, 0, 394, 397, 5, 80, 0, 0, 395, 397, 3, 64, 32, 0, 396, 394, 1, 0, 0, 0, 396, 395, 1, 0, 0, 0, 397, 61, 1, 0, 0, 0, 398, 441, 5, 45, 0, 0, 399, 400, 3, 98, 49, 0, 400, 401, 5, 67, 0, 0, 401, 441, 1, 0, 0, 0, 402, 441, 3, 96, 48, 0, 403, 441, 3, 98, 49, 0, 404, 441, 3, 92, 46, 0, 405, 441, 3, 64, 32, 0, 406, 441, 3, 100, 50, 0, 407, 408, 5, 65, 0, 0, 408, 413, 3, 94, 47, 0, 409, 410, 5, 34, 0, 0, 410, 412, 3, 94, 47, 0, 411, 409, 1, 0, 0, 0, 412, 415, 1, 0, 0, 0, 413, 411, 1, 0, 0, 0, 413, 414, 1, 0, 0, 0, 414, 416, 1, 0, 0, 0, 415, 413, 1, 0, 0, 0, 416, 417, 5, 66, 0, 0, 417, 441, 1, 0, 0, 0, 418, 419, 5, 65, 0, 0, 419, 424, 3, 92, 46, 0, 420, 421, 5, 34, 0, 0, 421, 423, 3, 92, 46, 0, 422, 420, 1, 0, 0, 0, 423, 426, 1, 0, 0, 0, 424, 422, 1, 0, 0, 0, 424, 425, 1, 0, 0, 0, 425, 427, 1, 0, 0, 0, 426, 424, 1, 0, 0, 0, 427, 428, 5, 66, 0, 0, 428, 441, 1, 0, 0, 0, 429, 430, 5, 65, 0, 0, 430, 435, 3, 100, 50, 0, 431, 432, 5, 34, 0, 0, 432, 434, 3, 100, 50, 0, 433, 431, 1, 0, 0, 0, 434, 437, 1, 0, 0, 0, 435, 433, 1, 0, 0, 0, 435, 436, 1, 0, 0, 0, 436, 438, 1, 0, 0, 0, 437, 435, 1, 0, 0, 0, 438, 439, 5, 66, 0, 0, 439, 441, 1, 0, 0, 0, 440, 398, 1, 0, 0, 0, 440, 399, 1, 0, 0, 0, 440, 402, 1, 0, 0, 0, 440, 403, 1, 0, 0, 0, 440, 404, 1, 0, 0, 0, 440, 405, 1, 0, 0, 0, 440, 406, 1, 0, 0, 0, 440, 407, 1, 0, 0, 0, 440, 418, 1, 0, 0, 0, 440, 429, 1, 0, 0, 0, 441, 63, 1, 0, 0, 0, 442, 445, 5, 48, 0, 0, 443, 445, 5, 64, 0, 0, 444, 442, 1, 0, 0, 0, 444, 443, 1, 0, 0, 0, 445, 65, 1, 0, 0, 0, 446, 449, 3, 58, 29, 0, 447, 449, 3, 64, 32, 0, 448, 446, 1, 0, 0, 0, 448, 447, 1, 0, 0, 0, 449, 67, 1, 0, 0, 0, 450, 451, 5, 9, 0, 0, 451, 452, 5, 27, 0, 0, 452, 69, 1, 0, 0, 0, 453, 454, 5, 14, 0, 0, 454, 459, 3, 72, 36, 0, 455, 456, 5, 34, 0, 0, 456, 458, 3, 72, 36, 0, 457, 455, 1, 0, 0, 0, 458, 461, 1, 0, 0, 0, 459, 457, 1, 0, 0, 0, 459, 460, 1, 0, 0, 0, 460, 71, 1, 0, 0, 0, 461, 459, 1, 0, 0, 0, 462, 464, 3, 10, 5, 0, 463, 465, 7, 4, 0, 0, 464, 463, 1, 0, 0, 0, 464, 465, 1, 0, 0, 0, 465, 468, 1, 0, 0, 0, 466, 467, 5, 46, 0, 0, 467, 469, 7, 5, 0, 0, 468, 466, 1, 0, 0, 0, 468, 469, 1, 0, 0, 0, 469, 73, 1, 0, 0, 0, 470, 471, 5, 8, 0, 0, 471, 472, 3, 56, 28, 0, 472, 75, 1, 0, 0, 0, 473, 474, 5, 2, 0, 0, 474, 475, 3, 56, 28, 0, 475, 77, 1, 0, 0, 0, 476, 477, 5, 11, 0, 0, 477, 482, 3, 80, 40, 0, 478, 479, 5, 34, 0, 0, 479, 481, 3, 80, 40, 0, 480, 478, 1, 0, 0, 0, 481, 484, 1, 0, 0, 0, 482, 480, 1, 0, 0, 0, 482, 483, 1, 0, 0, 0, 483, 79, 1, 0, 0, 0, 484, 482, 1, 0, 0, 0, 485, 486, 3, 54, 27, 0, 486, 487, 5, 84, 0, 0, 487, 488, 3, 54, 27, 0, 488, 81, 1, 0, 0, 0, 489, 490, 5, 1, 0, 0, 490, 491, 3, 20, 10, 0, 491, 493, 3, 100, 50, 0, 492, 494, 3, 88, 44, 0, 493, 492, 1, 0, 0, 0, 493, 494, 1, 0, 0, 0, 494, 83, 1, 0, 0, 0, 495, 496, 5, 7, 0, 0, 496, 497, 3, 20, 10, 0, 497, 498, 3, 100, 50, 0, 498, 85, 1, 0, 0, 0, 499, 500, 5, 10, 0, 0, 500, 501, 3, 52, 26, 0, 501, 87, 1, 0, 0, 0, 502, 507, 3, 90, 45, 0, 503, 504, 5, 34, 0, 0, 504, 506, 3, 90, 45, 0, 505, 503, 1, 0, 0, 0, 506, 509, 1, 0, 0, 0, 507, 505, 1, 0, 0, 0, 507, 508, 1, 0, 0, 0, 508, 89, 1, 0, 0, 0, 509, 507, 1, 0, 0, 0, 510, 511, 3, 58, 29, 0, 511, 512, 5, 32, 0, 0, 512, 513, 3, 62, 31, 0, 513, 91, 1, 0, 0, 0, 514, 515, 7, 6, 0, 0, 515, 93, 1, 0, 0, 0, 516, 519, 3, 96, 48, 0, 517, 519, 3, 98, 49, 0, 518, 516, 1, 0, 0, 0, 518, 517, 1, 0, 0, 0, 519, 95, 1, 0, 0, 0, 520, 522, 7, 0, 0, 0, 521, 520, 1, 0, 0, 0, 521, 522, 1, 0, 0, 0, 522, 523, 1, 0, 0, 0, 523, 524, 5, 28, 0, 0, 524, 97, 1, 0, 0, 0, 525, 527, 7, 0, 0, 0, 526, 525, 1, 0, 0, 0, 526, 527, 1, 0, 0, 0, 527, 528, 1, 0, 0, 0, 528, 529, 5, 27, 0, 0, 529, 99, 1, 0, 0, 0, 530, 531, 5, 26, 0, 0, 531, 101, 1, 0, 0, 0, 532, 533, 7, 7, 0, 0, 533, 103, 1, 0, 0, 0, 534, 535, 5, 5, 0, 0, 535, 536, 3, 106, 53, 0, 536, 105, 1, 0, 0, 0, 537, 538, 5, 65, 0, 0, 538, 539, 3, 2, 1, 0, 539, 540, 5, 66, 0, 0, 540, 107, 1, 0, 0, 0, 541, 542, 5, 13, 0, 0, 542, 543, 5, 100, 0, 0, 543, 109, 1, 0, 0, 0, 544, 545, 5, 3, 0, 0, 545, 548, 5, 90, 0, 0, 546, 547, 5, 88, 0, 0, 547, 549, 3, 54, 27, 0, 548, 546, 1, 0, 0, 0, 548, 549, 1, 0, 0, 0, 549, 559, 1, 0, 0, 0, 550, 551, 5, 89, 0, 0, 551, 556, 3, 112, 56, 0, 552, 553, 5, 34, 0, 0, 553, 555, 3, 112, 56, 0, 554, 552, 1, 0, 0, 0, 555, 558, 1, 0, 0, 0, 556, 554, 1, 0, 0, 0, 556, 557, 1, 0, 0, 0, 557, 560, 1, 0, 0, 0, 558, 556, 1, 0, 0, 0, 559, 550, 1, 0, 0, 0, 559, 560, 1, 0, 0, 0, 560, 111, 1, 0, 0, 0, 561, 562, 3, 54, 27, 0, 562, 563, 5, 32, 0, 0, 563, 565, 1, 0, 0, 0, 564, 561, 1, 0, 0, 0, 564, 565, 1, 0, 0, 0, 565, 566, 1, 0, 0, 0, 566, 567, 3, 54, 27, 0, 567, 113, 1, 0, 0, 0, 568, 569, 5, 18, 0, 0, 569, 570, 3, 34, 17, 0, 570, 571, 5, 88, 0, 0, 571, 572, 3, 56, 28, 0, 572, 115, 1, 0, 0, 0, 573, 574, 5, 17, 0, 0, 574, 577, 3, 28, 14, 0, 575, 576, 5, 29, 0, 0, 576, 578, 3, 28, 14, 0, 577, 575, 1, 0, 0, 0, 577, 578, 1, 0, 0, 0, 578, 117, 1, 0, 0, 0, 56, 129, 138, 156, 168, 177, 185, 191, 199, 201, 206, 213, 218, 229, 235, 243, 245, 256, 263, 274, 277, 291, 299, 307, 311, 318, 326, 334, 347, 351, 355, 362, 366, 373, 381, 389, 396, 413, 424, 435, 440, 444, 448, 459, 464, 468, 482, 493, 507, 518, 521, 526, 548, 556, 559, 564, 577] \ No newline at end of file +[4, 1, 120, 605, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 134, 8, 1, 10, 1, 12, 1, 137, 9, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 145, 8, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 163, 8, 3, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 175, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 182, 8, 5, 10, 5, 12, 5, 185, 9, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 192, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 198, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 206, 8, 5, 10, 5, 12, 5, 209, 9, 5, 1, 6, 1, 6, 3, 6, 213, 8, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 3, 6, 220, 8, 6, 1, 6, 1, 6, 1, 6, 3, 6, 225, 8, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 236, 8, 8, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 242, 8, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 5, 9, 250, 8, 9, 10, 9, 12, 9, 253, 9, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 3, 10, 263, 8, 10, 1, 10, 1, 10, 1, 10, 5, 10, 268, 8, 10, 10, 10, 12, 10, 271, 9, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 5, 11, 279, 8, 11, 10, 11, 12, 11, 282, 9, 11, 3, 11, 284, 8, 11, 1, 11, 1, 11, 1, 12, 1, 12, 3, 12, 290, 8, 12, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 5, 15, 300, 8, 15, 10, 15, 12, 15, 303, 9, 15, 1, 16, 1, 16, 1, 16, 3, 16, 308, 8, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 5, 17, 316, 8, 17, 10, 17, 12, 17, 319, 9, 17, 1, 17, 3, 17, 322, 8, 17, 1, 18, 1, 18, 1, 18, 3, 18, 327, 8, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 20, 1, 20, 1, 21, 1, 21, 3, 21, 337, 8, 21, 1, 22, 1, 22, 1, 22, 1, 22, 5, 22, 343, 8, 22, 10, 22, 12, 22, 346, 9, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 5, 24, 356, 8, 24, 10, 24, 12, 24, 359, 9, 24, 1, 24, 3, 24, 362, 8, 24, 1, 24, 1, 24, 3, 24, 366, 8, 24, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 3, 26, 373, 8, 26, 1, 26, 1, 26, 3, 26, 377, 8, 26, 1, 27, 1, 27, 1, 27, 5, 27, 382, 8, 27, 10, 27, 12, 27, 385, 9, 27, 1, 28, 1, 28, 1, 28, 3, 28, 390, 8, 28, 1, 29, 1, 29, 1, 29, 5, 29, 395, 8, 29, 10, 29, 12, 29, 398, 9, 29, 1, 30, 1, 30, 1, 30, 5, 30, 403, 8, 30, 10, 30, 12, 30, 406, 9, 30, 1, 31, 1, 31, 1, 31, 5, 31, 411, 8, 31, 10, 31, 12, 31, 414, 9, 31, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 3, 33, 421, 8, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 436, 8, 34, 10, 34, 12, 34, 439, 9, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 447, 8, 34, 10, 34, 12, 34, 450, 9, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 458, 8, 34, 10, 34, 12, 34, 461, 9, 34, 1, 34, 1, 34, 3, 34, 465, 8, 34, 1, 35, 1, 35, 3, 35, 469, 8, 35, 1, 36, 1, 36, 1, 36, 3, 36, 474, 8, 36, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 5, 38, 483, 8, 38, 10, 38, 12, 38, 486, 9, 38, 1, 39, 1, 39, 3, 39, 490, 8, 39, 1, 39, 1, 39, 3, 39, 494, 8, 39, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, 5, 42, 506, 8, 42, 10, 42, 12, 42, 509, 9, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 3, 44, 519, 8, 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 5, 47, 531, 8, 47, 10, 47, 12, 47, 534, 9, 47, 1, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 50, 1, 50, 3, 50, 544, 8, 50, 1, 51, 3, 51, 547, 8, 51, 1, 51, 1, 51, 1, 52, 3, 52, 552, 8, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 58, 3, 58, 574, 8, 58, 1, 58, 1, 58, 1, 58, 1, 58, 5, 58, 580, 8, 58, 10, 58, 12, 58, 583, 9, 58, 3, 58, 585, 8, 58, 1, 59, 1, 59, 1, 59, 3, 59, 590, 8, 59, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 61, 3, 61, 603, 8, 61, 1, 61, 0, 4, 2, 10, 18, 20, 62, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 0, 8, 1, 0, 58, 59, 1, 0, 60, 62, 2, 0, 25, 25, 76, 76, 1, 0, 67, 68, 2, 0, 30, 30, 34, 34, 2, 0, 37, 37, 40, 40, 2, 0, 36, 36, 50, 50, 2, 0, 51, 51, 53, 57, 631, 0, 124, 1, 0, 0, 0, 2, 127, 1, 0, 0, 0, 4, 144, 1, 0, 0, 0, 6, 162, 1, 0, 0, 0, 8, 164, 1, 0, 0, 0, 10, 197, 1, 0, 0, 0, 12, 224, 1, 0, 0, 0, 14, 226, 1, 0, 0, 0, 16, 235, 1, 0, 0, 0, 18, 241, 1, 0, 0, 0, 20, 262, 1, 0, 0, 0, 22, 272, 1, 0, 0, 0, 24, 289, 1, 0, 0, 0, 26, 291, 1, 0, 0, 0, 28, 293, 1, 0, 0, 0, 30, 296, 1, 0, 0, 0, 32, 307, 1, 0, 0, 0, 34, 311, 1, 0, 0, 0, 36, 326, 1, 0, 0, 0, 38, 330, 1, 0, 0, 0, 40, 332, 1, 0, 0, 0, 42, 336, 1, 0, 0, 0, 44, 338, 1, 0, 0, 0, 46, 347, 1, 0, 0, 0, 48, 351, 1, 0, 0, 0, 50, 367, 1, 0, 0, 0, 52, 370, 1, 0, 0, 0, 54, 378, 1, 0, 0, 0, 56, 386, 1, 0, 0, 0, 58, 391, 1, 0, 0, 0, 60, 399, 1, 0, 0, 0, 62, 407, 1, 0, 0, 0, 64, 415, 1, 0, 0, 0, 66, 420, 1, 0, 0, 0, 68, 464, 1, 0, 0, 0, 70, 468, 1, 0, 0, 0, 72, 473, 1, 0, 0, 0, 74, 475, 1, 0, 0, 0, 76, 478, 1, 0, 0, 0, 78, 487, 1, 0, 0, 0, 80, 495, 1, 0, 0, 0, 82, 498, 1, 0, 0, 0, 84, 501, 1, 0, 0, 0, 86, 510, 1, 0, 0, 0, 88, 514, 1, 0, 0, 0, 90, 520, 1, 0, 0, 0, 92, 524, 1, 0, 0, 0, 94, 527, 1, 0, 0, 0, 96, 535, 1, 0, 0, 0, 98, 539, 1, 0, 0, 0, 100, 543, 1, 0, 0, 0, 102, 546, 1, 0, 0, 0, 104, 551, 1, 0, 0, 0, 106, 555, 1, 0, 0, 0, 108, 557, 1, 0, 0, 0, 110, 559, 1, 0, 0, 0, 112, 562, 1, 0, 0, 0, 114, 566, 1, 0, 0, 0, 116, 569, 1, 0, 0, 0, 118, 589, 1, 0, 0, 0, 120, 593, 1, 0, 0, 0, 122, 598, 1, 0, 0, 0, 124, 125, 3, 2, 1, 0, 125, 126, 5, 0, 0, 1, 126, 1, 1, 0, 0, 0, 127, 128, 6, 1, -1, 0, 128, 129, 3, 4, 2, 0, 129, 135, 1, 0, 0, 0, 130, 131, 10, 1, 0, 0, 131, 132, 5, 24, 0, 0, 132, 134, 3, 6, 3, 0, 133, 130, 1, 0, 0, 0, 134, 137, 1, 0, 0, 0, 135, 133, 1, 0, 0, 0, 135, 136, 1, 0, 0, 0, 136, 3, 1, 0, 0, 0, 137, 135, 1, 0, 0, 0, 138, 145, 3, 110, 55, 0, 139, 145, 3, 34, 17, 0, 140, 145, 3, 28, 14, 0, 141, 145, 3, 114, 57, 0, 142, 143, 4, 2, 1, 0, 143, 145, 3, 48, 24, 0, 144, 138, 1, 0, 0, 0, 144, 139, 1, 0, 0, 0, 144, 140, 1, 0, 0, 0, 144, 141, 1, 0, 0, 0, 144, 142, 1, 0, 0, 0, 145, 5, 1, 0, 0, 0, 146, 163, 3, 50, 25, 0, 147, 163, 3, 8, 4, 0, 148, 163, 3, 80, 40, 0, 149, 163, 3, 74, 37, 0, 150, 163, 3, 52, 26, 0, 151, 163, 3, 76, 38, 0, 152, 163, 3, 82, 41, 0, 153, 163, 3, 84, 42, 0, 154, 163, 3, 88, 44, 0, 155, 163, 3, 90, 45, 0, 156, 163, 3, 116, 58, 0, 157, 163, 3, 92, 46, 0, 158, 159, 4, 3, 2, 0, 159, 163, 3, 122, 61, 0, 160, 161, 4, 3, 3, 0, 161, 163, 3, 120, 60, 0, 162, 146, 1, 0, 0, 0, 162, 147, 1, 0, 0, 0, 162, 148, 1, 0, 0, 0, 162, 149, 1, 0, 0, 0, 162, 150, 1, 0, 0, 0, 162, 151, 1, 0, 0, 0, 162, 152, 1, 0, 0, 0, 162, 153, 1, 0, 0, 0, 162, 154, 1, 0, 0, 0, 162, 155, 1, 0, 0, 0, 162, 156, 1, 0, 0, 0, 162, 157, 1, 0, 0, 0, 162, 158, 1, 0, 0, 0, 162, 160, 1, 0, 0, 0, 163, 7, 1, 0, 0, 0, 164, 165, 5, 16, 0, 0, 165, 166, 3, 10, 5, 0, 166, 9, 1, 0, 0, 0, 167, 168, 6, 5, -1, 0, 168, 169, 5, 43, 0, 0, 169, 198, 3, 10, 5, 8, 170, 198, 3, 16, 8, 0, 171, 198, 3, 12, 6, 0, 172, 174, 3, 16, 8, 0, 173, 175, 5, 43, 0, 0, 174, 173, 1, 0, 0, 0, 174, 175, 1, 0, 0, 0, 175, 176, 1, 0, 0, 0, 176, 177, 5, 38, 0, 0, 177, 178, 5, 42, 0, 0, 178, 183, 3, 16, 8, 0, 179, 180, 5, 33, 0, 0, 180, 182, 3, 16, 8, 0, 181, 179, 1, 0, 0, 0, 182, 185, 1, 0, 0, 0, 183, 181, 1, 0, 0, 0, 183, 184, 1, 0, 0, 0, 184, 186, 1, 0, 0, 0, 185, 183, 1, 0, 0, 0, 186, 187, 5, 49, 0, 0, 187, 198, 1, 0, 0, 0, 188, 189, 3, 16, 8, 0, 189, 191, 5, 39, 0, 0, 190, 192, 5, 43, 0, 0, 191, 190, 1, 0, 0, 0, 191, 192, 1, 0, 0, 0, 192, 193, 1, 0, 0, 0, 193, 194, 5, 44, 0, 0, 194, 198, 1, 0, 0, 0, 195, 196, 4, 5, 4, 0, 196, 198, 3, 14, 7, 0, 197, 167, 1, 0, 0, 0, 197, 170, 1, 0, 0, 0, 197, 171, 1, 0, 0, 0, 197, 172, 1, 0, 0, 0, 197, 188, 1, 0, 0, 0, 197, 195, 1, 0, 0, 0, 198, 207, 1, 0, 0, 0, 199, 200, 10, 5, 0, 0, 200, 201, 5, 29, 0, 0, 201, 206, 3, 10, 5, 6, 202, 203, 10, 4, 0, 0, 203, 204, 5, 46, 0, 0, 204, 206, 3, 10, 5, 5, 205, 199, 1, 0, 0, 0, 205, 202, 1, 0, 0, 0, 206, 209, 1, 0, 0, 0, 207, 205, 1, 0, 0, 0, 207, 208, 1, 0, 0, 0, 208, 11, 1, 0, 0, 0, 209, 207, 1, 0, 0, 0, 210, 212, 3, 16, 8, 0, 211, 213, 5, 43, 0, 0, 212, 211, 1, 0, 0, 0, 212, 213, 1, 0, 0, 0, 213, 214, 1, 0, 0, 0, 214, 215, 5, 41, 0, 0, 215, 216, 3, 106, 53, 0, 216, 225, 1, 0, 0, 0, 217, 219, 3, 16, 8, 0, 218, 220, 5, 43, 0, 0, 219, 218, 1, 0, 0, 0, 219, 220, 1, 0, 0, 0, 220, 221, 1, 0, 0, 0, 221, 222, 5, 48, 0, 0, 222, 223, 3, 106, 53, 0, 223, 225, 1, 0, 0, 0, 224, 210, 1, 0, 0, 0, 224, 217, 1, 0, 0, 0, 225, 13, 1, 0, 0, 0, 226, 227, 3, 16, 8, 0, 227, 228, 5, 63, 0, 0, 228, 229, 3, 106, 53, 0, 229, 15, 1, 0, 0, 0, 230, 236, 3, 18, 9, 0, 231, 232, 3, 18, 9, 0, 232, 233, 3, 108, 54, 0, 233, 234, 3, 18, 9, 0, 234, 236, 1, 0, 0, 0, 235, 230, 1, 0, 0, 0, 235, 231, 1, 0, 0, 0, 236, 17, 1, 0, 0, 0, 237, 238, 6, 9, -1, 0, 238, 242, 3, 20, 10, 0, 239, 240, 7, 0, 0, 0, 240, 242, 3, 18, 9, 3, 241, 237, 1, 0, 0, 0, 241, 239, 1, 0, 0, 0, 242, 251, 1, 0, 0, 0, 243, 244, 10, 2, 0, 0, 244, 245, 7, 1, 0, 0, 245, 250, 3, 18, 9, 3, 246, 247, 10, 1, 0, 0, 247, 248, 7, 0, 0, 0, 248, 250, 3, 18, 9, 2, 249, 243, 1, 0, 0, 0, 249, 246, 1, 0, 0, 0, 250, 253, 1, 0, 0, 0, 251, 249, 1, 0, 0, 0, 251, 252, 1, 0, 0, 0, 252, 19, 1, 0, 0, 0, 253, 251, 1, 0, 0, 0, 254, 255, 6, 10, -1, 0, 255, 263, 3, 68, 34, 0, 256, 263, 3, 58, 29, 0, 257, 263, 3, 22, 11, 0, 258, 259, 5, 42, 0, 0, 259, 260, 3, 10, 5, 0, 260, 261, 5, 49, 0, 0, 261, 263, 1, 0, 0, 0, 262, 254, 1, 0, 0, 0, 262, 256, 1, 0, 0, 0, 262, 257, 1, 0, 0, 0, 262, 258, 1, 0, 0, 0, 263, 269, 1, 0, 0, 0, 264, 265, 10, 1, 0, 0, 265, 266, 5, 32, 0, 0, 266, 268, 3, 26, 13, 0, 267, 264, 1, 0, 0, 0, 268, 271, 1, 0, 0, 0, 269, 267, 1, 0, 0, 0, 269, 270, 1, 0, 0, 0, 270, 21, 1, 0, 0, 0, 271, 269, 1, 0, 0, 0, 272, 273, 3, 24, 12, 0, 273, 283, 5, 42, 0, 0, 274, 284, 5, 60, 0, 0, 275, 280, 3, 10, 5, 0, 276, 277, 5, 33, 0, 0, 277, 279, 3, 10, 5, 0, 278, 276, 1, 0, 0, 0, 279, 282, 1, 0, 0, 0, 280, 278, 1, 0, 0, 0, 280, 281, 1, 0, 0, 0, 281, 284, 1, 0, 0, 0, 282, 280, 1, 0, 0, 0, 283, 274, 1, 0, 0, 0, 283, 275, 1, 0, 0, 0, 283, 284, 1, 0, 0, 0, 284, 285, 1, 0, 0, 0, 285, 286, 5, 49, 0, 0, 286, 23, 1, 0, 0, 0, 287, 290, 5, 63, 0, 0, 288, 290, 3, 72, 36, 0, 289, 287, 1, 0, 0, 0, 289, 288, 1, 0, 0, 0, 290, 25, 1, 0, 0, 0, 291, 292, 3, 64, 32, 0, 292, 27, 1, 0, 0, 0, 293, 294, 5, 12, 0, 0, 294, 295, 3, 30, 15, 0, 295, 29, 1, 0, 0, 0, 296, 301, 3, 32, 16, 0, 297, 298, 5, 33, 0, 0, 298, 300, 3, 32, 16, 0, 299, 297, 1, 0, 0, 0, 300, 303, 1, 0, 0, 0, 301, 299, 1, 0, 0, 0, 301, 302, 1, 0, 0, 0, 302, 31, 1, 0, 0, 0, 303, 301, 1, 0, 0, 0, 304, 305, 3, 58, 29, 0, 305, 306, 5, 31, 0, 0, 306, 308, 1, 0, 0, 0, 307, 304, 1, 0, 0, 0, 307, 308, 1, 0, 0, 0, 308, 309, 1, 0, 0, 0, 309, 310, 3, 10, 5, 0, 310, 33, 1, 0, 0, 0, 311, 312, 5, 6, 0, 0, 312, 317, 3, 36, 18, 0, 313, 314, 5, 33, 0, 0, 314, 316, 3, 36, 18, 0, 315, 313, 1, 0, 0, 0, 316, 319, 1, 0, 0, 0, 317, 315, 1, 0, 0, 0, 317, 318, 1, 0, 0, 0, 318, 321, 1, 0, 0, 0, 319, 317, 1, 0, 0, 0, 320, 322, 3, 42, 21, 0, 321, 320, 1, 0, 0, 0, 321, 322, 1, 0, 0, 0, 322, 35, 1, 0, 0, 0, 323, 324, 3, 38, 19, 0, 324, 325, 5, 104, 0, 0, 325, 327, 1, 0, 0, 0, 326, 323, 1, 0, 0, 0, 326, 327, 1, 0, 0, 0, 327, 328, 1, 0, 0, 0, 328, 329, 3, 40, 20, 0, 329, 37, 1, 0, 0, 0, 330, 331, 5, 76, 0, 0, 331, 39, 1, 0, 0, 0, 332, 333, 7, 2, 0, 0, 333, 41, 1, 0, 0, 0, 334, 337, 3, 44, 22, 0, 335, 337, 3, 46, 23, 0, 336, 334, 1, 0, 0, 0, 336, 335, 1, 0, 0, 0, 337, 43, 1, 0, 0, 0, 338, 339, 5, 75, 0, 0, 339, 344, 5, 76, 0, 0, 340, 341, 5, 33, 0, 0, 341, 343, 5, 76, 0, 0, 342, 340, 1, 0, 0, 0, 343, 346, 1, 0, 0, 0, 344, 342, 1, 0, 0, 0, 344, 345, 1, 0, 0, 0, 345, 45, 1, 0, 0, 0, 346, 344, 1, 0, 0, 0, 347, 348, 5, 65, 0, 0, 348, 349, 3, 44, 22, 0, 349, 350, 5, 66, 0, 0, 350, 47, 1, 0, 0, 0, 351, 352, 5, 19, 0, 0, 352, 357, 3, 36, 18, 0, 353, 354, 5, 33, 0, 0, 354, 356, 3, 36, 18, 0, 355, 353, 1, 0, 0, 0, 356, 359, 1, 0, 0, 0, 357, 355, 1, 0, 0, 0, 357, 358, 1, 0, 0, 0, 358, 361, 1, 0, 0, 0, 359, 357, 1, 0, 0, 0, 360, 362, 3, 54, 27, 0, 361, 360, 1, 0, 0, 0, 361, 362, 1, 0, 0, 0, 362, 365, 1, 0, 0, 0, 363, 364, 5, 28, 0, 0, 364, 366, 3, 30, 15, 0, 365, 363, 1, 0, 0, 0, 365, 366, 1, 0, 0, 0, 366, 49, 1, 0, 0, 0, 367, 368, 5, 4, 0, 0, 368, 369, 3, 30, 15, 0, 369, 51, 1, 0, 0, 0, 370, 372, 5, 15, 0, 0, 371, 373, 3, 54, 27, 0, 372, 371, 1, 0, 0, 0, 372, 373, 1, 0, 0, 0, 373, 376, 1, 0, 0, 0, 374, 375, 5, 28, 0, 0, 375, 377, 3, 30, 15, 0, 376, 374, 1, 0, 0, 0, 376, 377, 1, 0, 0, 0, 377, 53, 1, 0, 0, 0, 378, 383, 3, 56, 28, 0, 379, 380, 5, 33, 0, 0, 380, 382, 3, 56, 28, 0, 381, 379, 1, 0, 0, 0, 382, 385, 1, 0, 0, 0, 383, 381, 1, 0, 0, 0, 383, 384, 1, 0, 0, 0, 384, 55, 1, 0, 0, 0, 385, 383, 1, 0, 0, 0, 386, 389, 3, 32, 16, 0, 387, 388, 5, 16, 0, 0, 388, 390, 3, 10, 5, 0, 389, 387, 1, 0, 0, 0, 389, 390, 1, 0, 0, 0, 390, 57, 1, 0, 0, 0, 391, 396, 3, 72, 36, 0, 392, 393, 5, 35, 0, 0, 393, 395, 3, 72, 36, 0, 394, 392, 1, 0, 0, 0, 395, 398, 1, 0, 0, 0, 396, 394, 1, 0, 0, 0, 396, 397, 1, 0, 0, 0, 397, 59, 1, 0, 0, 0, 398, 396, 1, 0, 0, 0, 399, 404, 3, 66, 33, 0, 400, 401, 5, 35, 0, 0, 401, 403, 3, 66, 33, 0, 402, 400, 1, 0, 0, 0, 403, 406, 1, 0, 0, 0, 404, 402, 1, 0, 0, 0, 404, 405, 1, 0, 0, 0, 405, 61, 1, 0, 0, 0, 406, 404, 1, 0, 0, 0, 407, 412, 3, 60, 30, 0, 408, 409, 5, 33, 0, 0, 409, 411, 3, 60, 30, 0, 410, 408, 1, 0, 0, 0, 411, 414, 1, 0, 0, 0, 412, 410, 1, 0, 0, 0, 412, 413, 1, 0, 0, 0, 413, 63, 1, 0, 0, 0, 414, 412, 1, 0, 0, 0, 415, 416, 7, 3, 0, 0, 416, 65, 1, 0, 0, 0, 417, 421, 5, 80, 0, 0, 418, 419, 4, 33, 10, 0, 419, 421, 3, 70, 35, 0, 420, 417, 1, 0, 0, 0, 420, 418, 1, 0, 0, 0, 421, 67, 1, 0, 0, 0, 422, 465, 5, 44, 0, 0, 423, 424, 3, 104, 52, 0, 424, 425, 5, 67, 0, 0, 425, 465, 1, 0, 0, 0, 426, 465, 3, 102, 51, 0, 427, 465, 3, 104, 52, 0, 428, 465, 3, 98, 49, 0, 429, 465, 3, 70, 35, 0, 430, 465, 3, 106, 53, 0, 431, 432, 5, 65, 0, 0, 432, 437, 3, 100, 50, 0, 433, 434, 5, 33, 0, 0, 434, 436, 3, 100, 50, 0, 435, 433, 1, 0, 0, 0, 436, 439, 1, 0, 0, 0, 437, 435, 1, 0, 0, 0, 437, 438, 1, 0, 0, 0, 438, 440, 1, 0, 0, 0, 439, 437, 1, 0, 0, 0, 440, 441, 5, 66, 0, 0, 441, 465, 1, 0, 0, 0, 442, 443, 5, 65, 0, 0, 443, 448, 3, 98, 49, 0, 444, 445, 5, 33, 0, 0, 445, 447, 3, 98, 49, 0, 446, 444, 1, 0, 0, 0, 447, 450, 1, 0, 0, 0, 448, 446, 1, 0, 0, 0, 448, 449, 1, 0, 0, 0, 449, 451, 1, 0, 0, 0, 450, 448, 1, 0, 0, 0, 451, 452, 5, 66, 0, 0, 452, 465, 1, 0, 0, 0, 453, 454, 5, 65, 0, 0, 454, 459, 3, 106, 53, 0, 455, 456, 5, 33, 0, 0, 456, 458, 3, 106, 53, 0, 457, 455, 1, 0, 0, 0, 458, 461, 1, 0, 0, 0, 459, 457, 1, 0, 0, 0, 459, 460, 1, 0, 0, 0, 460, 462, 1, 0, 0, 0, 461, 459, 1, 0, 0, 0, 462, 463, 5, 66, 0, 0, 463, 465, 1, 0, 0, 0, 464, 422, 1, 0, 0, 0, 464, 423, 1, 0, 0, 0, 464, 426, 1, 0, 0, 0, 464, 427, 1, 0, 0, 0, 464, 428, 1, 0, 0, 0, 464, 429, 1, 0, 0, 0, 464, 430, 1, 0, 0, 0, 464, 431, 1, 0, 0, 0, 464, 442, 1, 0, 0, 0, 464, 453, 1, 0, 0, 0, 465, 69, 1, 0, 0, 0, 466, 469, 5, 47, 0, 0, 467, 469, 5, 64, 0, 0, 468, 466, 1, 0, 0, 0, 468, 467, 1, 0, 0, 0, 469, 71, 1, 0, 0, 0, 470, 474, 3, 64, 32, 0, 471, 472, 4, 36, 11, 0, 472, 474, 3, 70, 35, 0, 473, 470, 1, 0, 0, 0, 473, 471, 1, 0, 0, 0, 474, 73, 1, 0, 0, 0, 475, 476, 5, 9, 0, 0, 476, 477, 5, 26, 0, 0, 477, 75, 1, 0, 0, 0, 478, 479, 5, 14, 0, 0, 479, 484, 3, 78, 39, 0, 480, 481, 5, 33, 0, 0, 481, 483, 3, 78, 39, 0, 482, 480, 1, 0, 0, 0, 483, 486, 1, 0, 0, 0, 484, 482, 1, 0, 0, 0, 484, 485, 1, 0, 0, 0, 485, 77, 1, 0, 0, 0, 486, 484, 1, 0, 0, 0, 487, 489, 3, 10, 5, 0, 488, 490, 7, 4, 0, 0, 489, 488, 1, 0, 0, 0, 489, 490, 1, 0, 0, 0, 490, 493, 1, 0, 0, 0, 491, 492, 5, 45, 0, 0, 492, 494, 7, 5, 0, 0, 493, 491, 1, 0, 0, 0, 493, 494, 1, 0, 0, 0, 494, 79, 1, 0, 0, 0, 495, 496, 5, 8, 0, 0, 496, 497, 3, 62, 31, 0, 497, 81, 1, 0, 0, 0, 498, 499, 5, 2, 0, 0, 499, 500, 3, 62, 31, 0, 500, 83, 1, 0, 0, 0, 501, 502, 5, 11, 0, 0, 502, 507, 3, 86, 43, 0, 503, 504, 5, 33, 0, 0, 504, 506, 3, 86, 43, 0, 505, 503, 1, 0, 0, 0, 506, 509, 1, 0, 0, 0, 507, 505, 1, 0, 0, 0, 507, 508, 1, 0, 0, 0, 508, 85, 1, 0, 0, 0, 509, 507, 1, 0, 0, 0, 510, 511, 3, 60, 30, 0, 511, 512, 5, 84, 0, 0, 512, 513, 3, 60, 30, 0, 513, 87, 1, 0, 0, 0, 514, 515, 5, 1, 0, 0, 515, 516, 3, 20, 10, 0, 516, 518, 3, 106, 53, 0, 517, 519, 3, 94, 47, 0, 518, 517, 1, 0, 0, 0, 518, 519, 1, 0, 0, 0, 519, 89, 1, 0, 0, 0, 520, 521, 5, 7, 0, 0, 521, 522, 3, 20, 10, 0, 522, 523, 3, 106, 53, 0, 523, 91, 1, 0, 0, 0, 524, 525, 5, 10, 0, 0, 525, 526, 3, 58, 29, 0, 526, 93, 1, 0, 0, 0, 527, 532, 3, 96, 48, 0, 528, 529, 5, 33, 0, 0, 529, 531, 3, 96, 48, 0, 530, 528, 1, 0, 0, 0, 531, 534, 1, 0, 0, 0, 532, 530, 1, 0, 0, 0, 532, 533, 1, 0, 0, 0, 533, 95, 1, 0, 0, 0, 534, 532, 1, 0, 0, 0, 535, 536, 3, 64, 32, 0, 536, 537, 5, 31, 0, 0, 537, 538, 3, 68, 34, 0, 538, 97, 1, 0, 0, 0, 539, 540, 7, 6, 0, 0, 540, 99, 1, 0, 0, 0, 541, 544, 3, 102, 51, 0, 542, 544, 3, 104, 52, 0, 543, 541, 1, 0, 0, 0, 543, 542, 1, 0, 0, 0, 544, 101, 1, 0, 0, 0, 545, 547, 7, 0, 0, 0, 546, 545, 1, 0, 0, 0, 546, 547, 1, 0, 0, 0, 547, 548, 1, 0, 0, 0, 548, 549, 5, 27, 0, 0, 549, 103, 1, 0, 0, 0, 550, 552, 7, 0, 0, 0, 551, 550, 1, 0, 0, 0, 551, 552, 1, 0, 0, 0, 552, 553, 1, 0, 0, 0, 553, 554, 5, 26, 0, 0, 554, 105, 1, 0, 0, 0, 555, 556, 5, 25, 0, 0, 556, 107, 1, 0, 0, 0, 557, 558, 7, 7, 0, 0, 558, 109, 1, 0, 0, 0, 559, 560, 5, 5, 0, 0, 560, 561, 3, 112, 56, 0, 561, 111, 1, 0, 0, 0, 562, 563, 5, 65, 0, 0, 563, 564, 3, 2, 1, 0, 564, 565, 5, 66, 0, 0, 565, 113, 1, 0, 0, 0, 566, 567, 5, 13, 0, 0, 567, 568, 5, 100, 0, 0, 568, 115, 1, 0, 0, 0, 569, 570, 5, 3, 0, 0, 570, 573, 5, 90, 0, 0, 571, 572, 5, 88, 0, 0, 572, 574, 3, 60, 30, 0, 573, 571, 1, 0, 0, 0, 573, 574, 1, 0, 0, 0, 574, 584, 1, 0, 0, 0, 575, 576, 5, 89, 0, 0, 576, 581, 3, 118, 59, 0, 577, 578, 5, 33, 0, 0, 578, 580, 3, 118, 59, 0, 579, 577, 1, 0, 0, 0, 580, 583, 1, 0, 0, 0, 581, 579, 1, 0, 0, 0, 581, 582, 1, 0, 0, 0, 582, 585, 1, 0, 0, 0, 583, 581, 1, 0, 0, 0, 584, 575, 1, 0, 0, 0, 584, 585, 1, 0, 0, 0, 585, 117, 1, 0, 0, 0, 586, 587, 3, 60, 30, 0, 587, 588, 5, 31, 0, 0, 588, 590, 1, 0, 0, 0, 589, 586, 1, 0, 0, 0, 589, 590, 1, 0, 0, 0, 590, 591, 1, 0, 0, 0, 591, 592, 3, 60, 30, 0, 592, 119, 1, 0, 0, 0, 593, 594, 5, 18, 0, 0, 594, 595, 3, 36, 18, 0, 595, 596, 5, 88, 0, 0, 596, 597, 3, 62, 31, 0, 597, 121, 1, 0, 0, 0, 598, 599, 5, 17, 0, 0, 599, 602, 3, 54, 27, 0, 600, 601, 5, 28, 0, 0, 601, 603, 3, 30, 15, 0, 602, 600, 1, 0, 0, 0, 602, 603, 1, 0, 0, 0, 603, 123, 1, 0, 0, 0, 59, 135, 144, 162, 174, 183, 191, 197, 205, 207, 212, 219, 224, 235, 241, 249, 251, 262, 269, 280, 283, 289, 301, 307, 317, 321, 326, 336, 344, 357, 361, 365, 372, 376, 383, 389, 396, 404, 412, 420, 437, 448, 459, 464, 468, 473, 484, 489, 493, 507, 518, 532, 543, 546, 551, 573, 581, 584, 589, 602] \ No newline at end of file diff --git a/packages/kbn-esql-ast/src/antlr/esql_parser.tokens b/packages/kbn-esql-ast/src/antlr/esql_parser.tokens index 4fd37ab9900f2..4d1f426289149 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_parser.tokens +++ b/packages/kbn-esql-ast/src/antlr/esql_parser.tokens @@ -16,51 +16,51 @@ STATS=15 WHERE=16 DEV_INLINESTATS=17 DEV_LOOKUP=18 -DEV_MATCH=19 -DEV_METRICS=20 -UNKNOWN_CMD=21 -LINE_COMMENT=22 -MULTILINE_COMMENT=23 -WS=24 -PIPE=25 -QUOTED_STRING=26 -INTEGER_LITERAL=27 -DECIMAL_LITERAL=28 -BY=29 -AND=30 -ASC=31 -ASSIGN=32 -CAST_OP=33 -COMMA=34 -DESC=35 -DOT=36 -FALSE=37 -FIRST=38 -IN=39 -IS=40 -LAST=41 -LIKE=42 -LP=43 -NOT=44 -NULL=45 -NULLS=46 -OR=47 -PARAM=48 -RLIKE=49 -RP=50 -TRUE=51 -EQ=52 -CIEQ=53 -NEQ=54 -LT=55 -LTE=56 -GT=57 -GTE=58 -PLUS=59 -MINUS=60 -ASTERISK=61 -SLASH=62 -PERCENT=63 +DEV_METRICS=19 +UNKNOWN_CMD=20 +LINE_COMMENT=21 +MULTILINE_COMMENT=22 +WS=23 +PIPE=24 +QUOTED_STRING=25 +INTEGER_LITERAL=26 +DECIMAL_LITERAL=27 +BY=28 +AND=29 +ASC=30 +ASSIGN=31 +CAST_OP=32 +COMMA=33 +DESC=34 +DOT=35 +FALSE=36 +FIRST=37 +IN=38 +IS=39 +LAST=40 +LIKE=41 +LP=42 +NOT=43 +NULL=44 +NULLS=45 +OR=46 +PARAM=47 +RLIKE=48 +RP=49 +TRUE=50 +EQ=51 +CIEQ=52 +NEQ=53 +LT=54 +LTE=55 +GT=56 +GTE=57 +PLUS=58 +MINUS=59 +ASTERISK=60 +SLASH=61 +PERCENT=62 +MATCH=63 NAMED_OR_POSITIONAL_PARAM=64 OPENING_BRACKET=65 CLOSING_BRACKET=66 @@ -134,42 +134,43 @@ CLOSING_METRICS_WS=120 'sort'=14 'stats'=15 'where'=16 -'|'=25 -'by'=29 -'and'=30 -'asc'=31 -'='=32 -'::'=33 -','=34 -'desc'=35 -'.'=36 -'false'=37 -'first'=38 -'in'=39 -'is'=40 -'last'=41 -'like'=42 -'('=43 -'not'=44 -'null'=45 -'nulls'=46 -'or'=47 -'?'=48 -'rlike'=49 -')'=50 -'true'=51 -'=='=52 -'=~'=53 -'!='=54 -'<'=55 -'<='=56 -'>'=57 -'>='=58 -'+'=59 -'-'=60 -'*'=61 -'/'=62 -'%'=63 +'|'=24 +'by'=28 +'and'=29 +'asc'=30 +'='=31 +'::'=32 +','=33 +'desc'=34 +'.'=35 +'false'=36 +'first'=37 +'in'=38 +'is'=39 +'last'=40 +'like'=41 +'('=42 +'not'=43 +'null'=44 +'nulls'=45 +'or'=46 +'?'=47 +'rlike'=48 +')'=49 +'true'=50 +'=='=51 +'=~'=52 +'!='=53 +'<'=54 +'<='=55 +'>'=56 +'>='=57 +'+'=58 +'-'=59 +'*'=60 +'/'=61 +'%'=62 +'match'=63 ']'=66 'metadata'=75 'as'=84 diff --git a/packages/kbn-esql-ast/src/antlr/esql_parser.ts b/packages/kbn-esql-ast/src/antlr/esql_parser.ts index 41aea98166c97..b0af12e1ebc1e 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_parser.ts +++ b/packages/kbn-esql-ast/src/antlr/esql_parser.ts @@ -46,51 +46,51 @@ export default class esql_parser extends parser_config { public static readonly WHERE = 16; public static readonly DEV_INLINESTATS = 17; public static readonly DEV_LOOKUP = 18; - public static readonly DEV_MATCH = 19; - public static readonly DEV_METRICS = 20; - public static readonly UNKNOWN_CMD = 21; - public static readonly LINE_COMMENT = 22; - public static readonly MULTILINE_COMMENT = 23; - public static readonly WS = 24; - public static readonly PIPE = 25; - public static readonly QUOTED_STRING = 26; - public static readonly INTEGER_LITERAL = 27; - public static readonly DECIMAL_LITERAL = 28; - public static readonly BY = 29; - public static readonly AND = 30; - public static readonly ASC = 31; - public static readonly ASSIGN = 32; - public static readonly CAST_OP = 33; - public static readonly COMMA = 34; - public static readonly DESC = 35; - public static readonly DOT = 36; - public static readonly FALSE = 37; - public static readonly FIRST = 38; - public static readonly IN = 39; - public static readonly IS = 40; - public static readonly LAST = 41; - public static readonly LIKE = 42; - public static readonly LP = 43; - public static readonly NOT = 44; - public static readonly NULL = 45; - public static readonly NULLS = 46; - public static readonly OR = 47; - public static readonly PARAM = 48; - public static readonly RLIKE = 49; - public static readonly RP = 50; - public static readonly TRUE = 51; - public static readonly EQ = 52; - public static readonly CIEQ = 53; - public static readonly NEQ = 54; - public static readonly LT = 55; - public static readonly LTE = 56; - public static readonly GT = 57; - public static readonly GTE = 58; - public static readonly PLUS = 59; - public static readonly MINUS = 60; - public static readonly ASTERISK = 61; - public static readonly SLASH = 62; - public static readonly PERCENT = 63; + public static readonly DEV_METRICS = 19; + public static readonly UNKNOWN_CMD = 20; + public static readonly LINE_COMMENT = 21; + public static readonly MULTILINE_COMMENT = 22; + public static readonly WS = 23; + public static readonly PIPE = 24; + public static readonly QUOTED_STRING = 25; + public static readonly INTEGER_LITERAL = 26; + public static readonly DECIMAL_LITERAL = 27; + public static readonly BY = 28; + public static readonly AND = 29; + public static readonly ASC = 30; + public static readonly ASSIGN = 31; + public static readonly CAST_OP = 32; + public static readonly COMMA = 33; + public static readonly DESC = 34; + public static readonly DOT = 35; + public static readonly FALSE = 36; + public static readonly FIRST = 37; + public static readonly IN = 38; + public static readonly IS = 39; + public static readonly LAST = 40; + public static readonly LIKE = 41; + public static readonly LP = 42; + public static readonly NOT = 43; + public static readonly NULL = 44; + public static readonly NULLS = 45; + public static readonly OR = 46; + public static readonly PARAM = 47; + public static readonly RLIKE = 48; + public static readonly RP = 49; + public static readonly TRUE = 50; + public static readonly EQ = 51; + public static readonly CIEQ = 52; + public static readonly NEQ = 53; + public static readonly LT = 54; + public static readonly LTE = 55; + public static readonly GT = 56; + public static readonly GTE = 57; + public static readonly PLUS = 58; + public static readonly MINUS = 59; + public static readonly ASTERISK = 60; + public static readonly SLASH = 61; + public static readonly PERCENT = 62; + public static readonly MATCH = 63; public static readonly NAMED_OR_POSITIONAL_PARAM = 64; public static readonly OPENING_BRACKET = 65; public static readonly CLOSING_BRACKET = 66; @@ -161,53 +161,56 @@ export default class esql_parser extends parser_config { public static readonly RULE_operatorExpression = 9; public static readonly RULE_primaryExpression = 10; public static readonly RULE_functionExpression = 11; - public static readonly RULE_dataType = 12; - public static readonly RULE_rowCommand = 13; - public static readonly RULE_fields = 14; - public static readonly RULE_field = 15; - public static readonly RULE_fromCommand = 16; - public static readonly RULE_indexPattern = 17; - public static readonly RULE_clusterString = 18; - public static readonly RULE_indexString = 19; - public static readonly RULE_metadata = 20; - public static readonly RULE_metadataOption = 21; - public static readonly RULE_deprecated_metadata = 22; - public static readonly RULE_metricsCommand = 23; - public static readonly RULE_evalCommand = 24; - public static readonly RULE_statsCommand = 25; - public static readonly RULE_qualifiedName = 26; - public static readonly RULE_qualifiedNamePattern = 27; - public static readonly RULE_qualifiedNamePatterns = 28; - public static readonly RULE_identifier = 29; - public static readonly RULE_identifierPattern = 30; - public static readonly RULE_constant = 31; - public static readonly RULE_parameter = 32; - public static readonly RULE_identifierOrParameter = 33; - public static readonly RULE_limitCommand = 34; - public static readonly RULE_sortCommand = 35; - public static readonly RULE_orderExpression = 36; - public static readonly RULE_keepCommand = 37; - public static readonly RULE_dropCommand = 38; - public static readonly RULE_renameCommand = 39; - public static readonly RULE_renameClause = 40; - public static readonly RULE_dissectCommand = 41; - public static readonly RULE_grokCommand = 42; - public static readonly RULE_mvExpandCommand = 43; - public static readonly RULE_commandOptions = 44; - public static readonly RULE_commandOption = 45; - public static readonly RULE_booleanValue = 46; - public static readonly RULE_numericValue = 47; - public static readonly RULE_decimalValue = 48; - public static readonly RULE_integerValue = 49; - public static readonly RULE_string = 50; - public static readonly RULE_comparisonOperator = 51; - public static readonly RULE_explainCommand = 52; - public static readonly RULE_subqueryExpression = 53; - public static readonly RULE_showCommand = 54; - public static readonly RULE_enrichCommand = 55; - public static readonly RULE_enrichWithClause = 56; - public static readonly RULE_lookupCommand = 57; - public static readonly RULE_inlinestatsCommand = 58; + public static readonly RULE_functionName = 12; + public static readonly RULE_dataType = 13; + public static readonly RULE_rowCommand = 14; + public static readonly RULE_fields = 15; + public static readonly RULE_field = 16; + public static readonly RULE_fromCommand = 17; + public static readonly RULE_indexPattern = 18; + public static readonly RULE_clusterString = 19; + public static readonly RULE_indexString = 20; + public static readonly RULE_metadata = 21; + public static readonly RULE_metadataOption = 22; + public static readonly RULE_deprecated_metadata = 23; + public static readonly RULE_metricsCommand = 24; + public static readonly RULE_evalCommand = 25; + public static readonly RULE_statsCommand = 26; + public static readonly RULE_aggFields = 27; + public static readonly RULE_aggField = 28; + public static readonly RULE_qualifiedName = 29; + public static readonly RULE_qualifiedNamePattern = 30; + public static readonly RULE_qualifiedNamePatterns = 31; + public static readonly RULE_identifier = 32; + public static readonly RULE_identifierPattern = 33; + public static readonly RULE_constant = 34; + public static readonly RULE_parameter = 35; + public static readonly RULE_identifierOrParameter = 36; + public static readonly RULE_limitCommand = 37; + public static readonly RULE_sortCommand = 38; + public static readonly RULE_orderExpression = 39; + public static readonly RULE_keepCommand = 40; + public static readonly RULE_dropCommand = 41; + public static readonly RULE_renameCommand = 42; + public static readonly RULE_renameClause = 43; + public static readonly RULE_dissectCommand = 44; + public static readonly RULE_grokCommand = 45; + public static readonly RULE_mvExpandCommand = 46; + public static readonly RULE_commandOptions = 47; + public static readonly RULE_commandOption = 48; + public static readonly RULE_booleanValue = 49; + public static readonly RULE_numericValue = 50; + public static readonly RULE_decimalValue = 51; + public static readonly RULE_integerValue = 52; + public static readonly RULE_string = 53; + public static readonly RULE_comparisonOperator = 54; + public static readonly RULE_explainCommand = 55; + public static readonly RULE_subqueryExpression = 56; + public static readonly RULE_showCommand = 57; + public static readonly RULE_enrichCommand = 58; + public static readonly RULE_enrichWithClause = 59; + public static readonly RULE_lookupCommand = 60; + public static readonly RULE_inlinestatsCommand = 61; public static readonly literalNames: (string | null)[] = [ null, "'dissect'", "'drop'", "'enrich'", "'eval'", "'explain'", @@ -221,26 +224,26 @@ export default class esql_parser extends parser_config { null, null, null, null, null, null, - null, "'|'", + "'|'", null, null, null, - null, "'by'", - "'and'", "'asc'", - "'='", "'::'", - "','", "'desc'", - "'.'", "'false'", - "'first'", "'in'", - "'is'", "'last'", - "'like'", "'('", - "'not'", "'null'", - "'nulls'", "'or'", - "'?'", "'rlike'", - "')'", "'true'", - "'=='", "'=~'", - "'!='", "'<'", - "'<='", "'>'", - "'>='", "'+'", - "'-'", "'*'", - "'/'", "'%'", + "'by'", "'and'", + "'asc'", "'='", + "'::'", "','", + "'desc'", "'.'", + "'false'", "'first'", + "'in'", "'is'", + "'last'", "'like'", + "'('", "'not'", + "'null'", "'nulls'", + "'or'", "'?'", + "'rlike'", "')'", + "'true'", "'=='", + "'=~'", "'!='", + "'<'", "'<='", + "'>'", "'>='", + "'+'", "'-'", + "'*'", "'/'", + "'%'", "'match'", null, null, "']'", null, null, null, @@ -273,7 +276,6 @@ export default class esql_parser extends parser_config { "STATS", "WHERE", "DEV_INLINESTATS", "DEV_LOOKUP", - "DEV_MATCH", "DEV_METRICS", "UNKNOWN_CMD", "LINE_COMMENT", @@ -300,7 +302,7 @@ export default class esql_parser extends parser_config { "GTE", "PLUS", "MINUS", "ASTERISK", "SLASH", "PERCENT", - "NAMED_OR_POSITIONAL_PARAM", + "MATCH", "NAMED_OR_POSITIONAL_PARAM", "OPENING_BRACKET", "CLOSING_BRACKET", "UNQUOTED_IDENTIFIER", @@ -358,16 +360,16 @@ export default class esql_parser extends parser_config { "singleStatement", "query", "sourceCommand", "processingCommand", "whereCommand", "booleanExpression", "regexBooleanExpression", "matchBooleanExpression", "valueExpression", "operatorExpression", "primaryExpression", "functionExpression", - "dataType", "rowCommand", "fields", "field", "fromCommand", "indexPattern", - "clusterString", "indexString", "metadata", "metadataOption", "deprecated_metadata", - "metricsCommand", "evalCommand", "statsCommand", "qualifiedName", "qualifiedNamePattern", - "qualifiedNamePatterns", "identifier", "identifierPattern", "constant", - "parameter", "identifierOrParameter", "limitCommand", "sortCommand", "orderExpression", - "keepCommand", "dropCommand", "renameCommand", "renameClause", "dissectCommand", - "grokCommand", "mvExpandCommand", "commandOptions", "commandOption", "booleanValue", - "numericValue", "decimalValue", "integerValue", "string", "comparisonOperator", - "explainCommand", "subqueryExpression", "showCommand", "enrichCommand", - "enrichWithClause", "lookupCommand", "inlinestatsCommand", + "functionName", "dataType", "rowCommand", "fields", "field", "fromCommand", + "indexPattern", "clusterString", "indexString", "metadata", "metadataOption", + "deprecated_metadata", "metricsCommand", "evalCommand", "statsCommand", + "aggFields", "aggField", "qualifiedName", "qualifiedNamePattern", "qualifiedNamePatterns", + "identifier", "identifierPattern", "constant", "parameter", "identifierOrParameter", + "limitCommand", "sortCommand", "orderExpression", "keepCommand", "dropCommand", + "renameCommand", "renameClause", "dissectCommand", "grokCommand", "mvExpandCommand", + "commandOptions", "commandOption", "booleanValue", "numericValue", "decimalValue", + "integerValue", "string", "comparisonOperator", "explainCommand", "subqueryExpression", + "showCommand", "enrichCommand", "enrichWithClause", "lookupCommand", "inlinestatsCommand", ]; public get grammarFileName(): string { return "esql_parser.g4"; } public get literalNames(): (string | null)[] { return esql_parser.literalNames; } @@ -390,9 +392,9 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 118; + this.state = 124; this.query(0); - this.state = 119; + this.state = 125; this.match(esql_parser.EOF); } } @@ -434,11 +436,11 @@ export default class esql_parser extends parser_config { this._ctx = localctx; _prevctx = localctx; - this.state = 122; + this.state = 128; this.sourceCommand(); } this._ctx.stop = this._input.LT(-1); - this.state = 129; + this.state = 135; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 0, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { @@ -451,18 +453,18 @@ export default class esql_parser extends parser_config { { localctx = new CompositeQueryContext(this, new QueryContext(this, _parentctx, _parentState)); this.pushNewRecursionContext(localctx, _startState, esql_parser.RULE_query); - this.state = 124; + this.state = 130; if (!(this.precpred(this._ctx, 1))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 1)"); } - this.state = 125; + this.state = 131; this.match(esql_parser.PIPE); - this.state = 126; + this.state = 132; this.processingCommand(); } } } - this.state = 131; + this.state = 137; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 0, this._ctx); } @@ -487,45 +489,45 @@ export default class esql_parser extends parser_config { let localctx: SourceCommandContext = new SourceCommandContext(this, this._ctx, this.state); this.enterRule(localctx, 4, esql_parser.RULE_sourceCommand); try { - this.state = 138; + this.state = 144; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 1, this._ctx) ) { case 1: this.enterOuterAlt(localctx, 1); { - this.state = 132; + this.state = 138; this.explainCommand(); } break; case 2: this.enterOuterAlt(localctx, 2); { - this.state = 133; + this.state = 139; this.fromCommand(); } break; case 3: this.enterOuterAlt(localctx, 3); { - this.state = 134; + this.state = 140; this.rowCommand(); } break; case 4: this.enterOuterAlt(localctx, 4); { - this.state = 135; + this.state = 141; this.showCommand(); } break; case 5: this.enterOuterAlt(localctx, 5); { - this.state = 136; + this.state = 142; if (!(this.isDevVersion())) { throw this.createFailedPredicateException("this.isDevVersion()"); } - this.state = 137; + this.state = 143; this.metricsCommand(); } break; @@ -550,112 +552,112 @@ export default class esql_parser extends parser_config { let localctx: ProcessingCommandContext = new ProcessingCommandContext(this, this._ctx, this.state); this.enterRule(localctx, 6, esql_parser.RULE_processingCommand); try { - this.state = 156; + this.state = 162; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 2, this._ctx) ) { case 1: this.enterOuterAlt(localctx, 1); { - this.state = 140; + this.state = 146; this.evalCommand(); } break; case 2: this.enterOuterAlt(localctx, 2); { - this.state = 141; + this.state = 147; this.whereCommand(); } break; case 3: this.enterOuterAlt(localctx, 3); { - this.state = 142; + this.state = 148; this.keepCommand(); } break; case 4: this.enterOuterAlt(localctx, 4); { - this.state = 143; + this.state = 149; this.limitCommand(); } break; case 5: this.enterOuterAlt(localctx, 5); { - this.state = 144; + this.state = 150; this.statsCommand(); } break; case 6: this.enterOuterAlt(localctx, 6); { - this.state = 145; + this.state = 151; this.sortCommand(); } break; case 7: this.enterOuterAlt(localctx, 7); { - this.state = 146; + this.state = 152; this.dropCommand(); } break; case 8: this.enterOuterAlt(localctx, 8); { - this.state = 147; + this.state = 153; this.renameCommand(); } break; case 9: this.enterOuterAlt(localctx, 9); { - this.state = 148; + this.state = 154; this.dissectCommand(); } break; case 10: this.enterOuterAlt(localctx, 10); { - this.state = 149; + this.state = 155; this.grokCommand(); } break; case 11: this.enterOuterAlt(localctx, 11); { - this.state = 150; + this.state = 156; this.enrichCommand(); } break; case 12: this.enterOuterAlt(localctx, 12); { - this.state = 151; + this.state = 157; this.mvExpandCommand(); } break; case 13: this.enterOuterAlt(localctx, 13); { - this.state = 152; + this.state = 158; if (!(this.isDevVersion())) { throw this.createFailedPredicateException("this.isDevVersion()"); } - this.state = 153; + this.state = 159; this.inlinestatsCommand(); } break; case 14: this.enterOuterAlt(localctx, 14); { - this.state = 154; + this.state = 160; if (!(this.isDevVersion())) { throw this.createFailedPredicateException("this.isDevVersion()"); } - this.state = 155; + this.state = 161; this.lookupCommand(); } break; @@ -682,9 +684,9 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 158; + this.state = 164; this.match(esql_parser.WHERE); - this.state = 159; + this.state = 165; this.booleanExpression(0); } } @@ -722,7 +724,7 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 191; + this.state = 197; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 6, this._ctx) ) { case 1: @@ -731,9 +733,9 @@ export default class esql_parser extends parser_config { this._ctx = localctx; _prevctx = localctx; - this.state = 162; + this.state = 168; this.match(esql_parser.NOT); - this.state = 163; + this.state = 169; this.booleanExpression(8); } break; @@ -742,7 +744,7 @@ export default class esql_parser extends parser_config { localctx = new BooleanDefaultContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 164; + this.state = 170; this.valueExpression(); } break; @@ -751,7 +753,7 @@ export default class esql_parser extends parser_config { localctx = new RegexExpressionContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 165; + this.state = 171; this.regexBooleanExpression(); } break; @@ -760,41 +762,41 @@ export default class esql_parser extends parser_config { localctx = new LogicalInContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 166; + this.state = 172; this.valueExpression(); - this.state = 168; + this.state = 174; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===44) { + if (_la===43) { { - this.state = 167; + this.state = 173; this.match(esql_parser.NOT); } } - this.state = 170; + this.state = 176; this.match(esql_parser.IN); - this.state = 171; + this.state = 177; this.match(esql_parser.LP); - this.state = 172; + this.state = 178; this.valueExpression(); - this.state = 177; + this.state = 183; this._errHandler.sync(this); _la = this._input.LA(1); - while (_la===34) { + while (_la===33) { { { - this.state = 173; + this.state = 179; this.match(esql_parser.COMMA); - this.state = 174; + this.state = 180; this.valueExpression(); } } - this.state = 179; + this.state = 185; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 180; + this.state = 186; this.match(esql_parser.RP); } break; @@ -803,21 +805,21 @@ export default class esql_parser extends parser_config { localctx = new IsNullContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 182; + this.state = 188; this.valueExpression(); - this.state = 183; + this.state = 189; this.match(esql_parser.IS); - this.state = 185; + this.state = 191; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===44) { + if (_la===43) { { - this.state = 184; + this.state = 190; this.match(esql_parser.NOT); } } - this.state = 187; + this.state = 193; this.match(esql_parser.NULL); } break; @@ -826,17 +828,17 @@ export default class esql_parser extends parser_config { localctx = new MatchExpressionContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 189; + this.state = 195; if (!(this.isDevVersion())) { throw this.createFailedPredicateException("this.isDevVersion()"); } - this.state = 190; + this.state = 196; this.matchBooleanExpression(); } break; } this._ctx.stop = this._input.LT(-1); - this.state = 201; + this.state = 207; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 8, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { @@ -846,7 +848,7 @@ export default class esql_parser extends parser_config { } _prevctx = localctx; { - this.state = 199; + this.state = 205; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 7, this._ctx) ) { case 1: @@ -854,13 +856,13 @@ export default class esql_parser extends parser_config { localctx = new LogicalBinaryContext(this, new BooleanExpressionContext(this, _parentctx, _parentState)); (localctx as LogicalBinaryContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, esql_parser.RULE_booleanExpression); - this.state = 193; + this.state = 199; if (!(this.precpred(this._ctx, 5))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 5)"); } - this.state = 194; + this.state = 200; (localctx as LogicalBinaryContext)._operator = this.match(esql_parser.AND); - this.state = 195; + this.state = 201; (localctx as LogicalBinaryContext)._right = this.booleanExpression(6); } break; @@ -869,20 +871,20 @@ export default class esql_parser extends parser_config { localctx = new LogicalBinaryContext(this, new BooleanExpressionContext(this, _parentctx, _parentState)); (localctx as LogicalBinaryContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, esql_parser.RULE_booleanExpression); - this.state = 196; + this.state = 202; if (!(this.precpred(this._ctx, 4))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 4)"); } - this.state = 197; + this.state = 203; (localctx as LogicalBinaryContext)._operator = this.match(esql_parser.OR); - this.state = 198; + this.state = 204; (localctx as LogicalBinaryContext)._right = this.booleanExpression(5); } break; } } } - this.state = 203; + this.state = 209; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 8, this._ctx); } @@ -908,48 +910,48 @@ export default class esql_parser extends parser_config { this.enterRule(localctx, 12, esql_parser.RULE_regexBooleanExpression); let _la: number; try { - this.state = 218; + this.state = 224; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 11, this._ctx) ) { case 1: this.enterOuterAlt(localctx, 1); { - this.state = 204; + this.state = 210; this.valueExpression(); - this.state = 206; + this.state = 212; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===44) { + if (_la===43) { { - this.state = 205; + this.state = 211; this.match(esql_parser.NOT); } } - this.state = 208; + this.state = 214; localctx._kind = this.match(esql_parser.LIKE); - this.state = 209; + this.state = 215; localctx._pattern = this.string_(); } break; case 2: this.enterOuterAlt(localctx, 2); { - this.state = 211; + this.state = 217; this.valueExpression(); - this.state = 213; + this.state = 219; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===44) { + if (_la===43) { { - this.state = 212; + this.state = 218; this.match(esql_parser.NOT); } } - this.state = 215; + this.state = 221; localctx._kind = this.match(esql_parser.RLIKE); - this.state = 216; + this.state = 222; localctx._pattern = this.string_(); } break; @@ -976,11 +978,11 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 220; + this.state = 226; this.valueExpression(); - this.state = 221; - this.match(esql_parser.DEV_MATCH); - this.state = 222; + this.state = 227; + this.match(esql_parser.MATCH); + this.state = 228; localctx._queryString = this.string_(); } } @@ -1003,14 +1005,14 @@ export default class esql_parser extends parser_config { let localctx: ValueExpressionContext = new ValueExpressionContext(this, this._ctx, this.state); this.enterRule(localctx, 16, esql_parser.RULE_valueExpression); try { - this.state = 229; + this.state = 235; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 12, this._ctx) ) { case 1: localctx = new ValueExpressionDefaultContext(this, localctx); this.enterOuterAlt(localctx, 1); { - this.state = 224; + this.state = 230; this.operatorExpression(0); } break; @@ -1018,11 +1020,11 @@ export default class esql_parser extends parser_config { localctx = new ComparisonContext(this, localctx); this.enterOuterAlt(localctx, 2); { - this.state = 225; + this.state = 231; (localctx as ComparisonContext)._left = this.operatorExpression(0); - this.state = 226; + this.state = 232; this.comparisonOperator(); - this.state = 227; + this.state = 233; (localctx as ComparisonContext)._right = this.operatorExpression(0); } break; @@ -1062,7 +1064,7 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 235; + this.state = 241; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 13, this._ctx) ) { case 1: @@ -1071,7 +1073,7 @@ export default class esql_parser extends parser_config { this._ctx = localctx; _prevctx = localctx; - this.state = 232; + this.state = 238; this.primaryExpression(0); } break; @@ -1080,23 +1082,23 @@ export default class esql_parser extends parser_config { localctx = new ArithmeticUnaryContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 233; + this.state = 239; (localctx as ArithmeticUnaryContext)._operator = this._input.LT(1); _la = this._input.LA(1); - if(!(_la===59 || _la===60)) { + if(!(_la===58 || _la===59)) { (localctx as ArithmeticUnaryContext)._operator = this._errHandler.recoverInline(this); } else { this._errHandler.reportMatch(this); this.consume(); } - this.state = 234; + this.state = 240; this.operatorExpression(3); } break; } this._ctx.stop = this._input.LT(-1); - this.state = 245; + this.state = 251; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 15, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { @@ -1106,7 +1108,7 @@ export default class esql_parser extends parser_config { } _prevctx = localctx; { - this.state = 243; + this.state = 249; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 14, this._ctx) ) { case 1: @@ -1114,21 +1116,21 @@ export default class esql_parser extends parser_config { localctx = new ArithmeticBinaryContext(this, new OperatorExpressionContext(this, _parentctx, _parentState)); (localctx as ArithmeticBinaryContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, esql_parser.RULE_operatorExpression); - this.state = 237; + this.state = 243; if (!(this.precpred(this._ctx, 2))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 2)"); } - this.state = 238; + this.state = 244; (localctx as ArithmeticBinaryContext)._operator = this._input.LT(1); _la = this._input.LA(1); - if(!(((((_la - 61)) & ~0x1F) === 0 && ((1 << (_la - 61)) & 7) !== 0))) { + if(!(((((_la - 60)) & ~0x1F) === 0 && ((1 << (_la - 60)) & 7) !== 0))) { (localctx as ArithmeticBinaryContext)._operator = this._errHandler.recoverInline(this); } else { this._errHandler.reportMatch(this); this.consume(); } - this.state = 239; + this.state = 245; (localctx as ArithmeticBinaryContext)._right = this.operatorExpression(3); } break; @@ -1137,28 +1139,28 @@ export default class esql_parser extends parser_config { localctx = new ArithmeticBinaryContext(this, new OperatorExpressionContext(this, _parentctx, _parentState)); (localctx as ArithmeticBinaryContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, esql_parser.RULE_operatorExpression); - this.state = 240; + this.state = 246; if (!(this.precpred(this._ctx, 1))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 1)"); } - this.state = 241; + this.state = 247; (localctx as ArithmeticBinaryContext)._operator = this._input.LT(1); _la = this._input.LA(1); - if(!(_la===59 || _la===60)) { + if(!(_la===58 || _la===59)) { (localctx as ArithmeticBinaryContext)._operator = this._errHandler.recoverInline(this); } else { this._errHandler.reportMatch(this); this.consume(); } - this.state = 242; + this.state = 248; (localctx as ArithmeticBinaryContext)._right = this.operatorExpression(2); } break; } } } - this.state = 247; + this.state = 253; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 15, this._ctx); } @@ -1197,7 +1199,7 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 256; + this.state = 262; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 16, this._ctx) ) { case 1: @@ -1206,7 +1208,7 @@ export default class esql_parser extends parser_config { this._ctx = localctx; _prevctx = localctx; - this.state = 249; + this.state = 255; this.constant(); } break; @@ -1215,7 +1217,7 @@ export default class esql_parser extends parser_config { localctx = new DereferenceContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 250; + this.state = 256; this.qualifiedName(); } break; @@ -1224,7 +1226,7 @@ export default class esql_parser extends parser_config { localctx = new FunctionContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 251; + this.state = 257; this.functionExpression(); } break; @@ -1233,17 +1235,17 @@ export default class esql_parser extends parser_config { localctx = new ParenthesizedExpressionContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 252; + this.state = 258; this.match(esql_parser.LP); - this.state = 253; + this.state = 259; this.booleanExpression(0); - this.state = 254; + this.state = 260; this.match(esql_parser.RP); } break; } this._ctx.stop = this._input.LT(-1); - this.state = 263; + this.state = 269; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 17, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { @@ -1256,18 +1258,18 @@ export default class esql_parser extends parser_config { { localctx = new InlineCastContext(this, new PrimaryExpressionContext(this, _parentctx, _parentState)); this.pushNewRecursionContext(localctx, _startState, esql_parser.RULE_primaryExpression); - this.state = 258; + this.state = 264; if (!(this.precpred(this._ctx, 1))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 1)"); } - this.state = 259; + this.state = 265; this.match(esql_parser.CAST_OP); - this.state = 260; + this.state = 266; this.dataType(); } } } - this.state = 265; + this.state = 271; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 17, this._ctx); } @@ -1295,37 +1297,37 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 266; - this.identifierOrParameter(); - this.state = 267; + this.state = 272; + this.functionName(); + this.state = 273; this.match(esql_parser.LP); - this.state = 277; + this.state = 283; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 19, this._ctx) ) { case 1: { - this.state = 268; + this.state = 274; this.match(esql_parser.ASTERISK); } break; case 2: { { - this.state = 269; + this.state = 275; this.booleanExpression(0); - this.state = 274; + this.state = 280; this._errHandler.sync(this); _la = this._input.LA(1); - while (_la===34) { + while (_la===33) { { { - this.state = 270; + this.state = 276; this.match(esql_parser.COMMA); - this.state = 271; + this.state = 277; this.booleanExpression(0); } } - this.state = 276; + this.state = 282; this._errHandler.sync(this); _la = this._input.LA(1); } @@ -1333,7 +1335,7 @@ export default class esql_parser extends parser_config { } break; } - this.state = 279; + this.state = 285; this.match(esql_parser.RP); } } @@ -1352,14 +1354,52 @@ export default class esql_parser extends parser_config { return localctx; } // @RuleVersion(0) + public functionName(): FunctionNameContext { + let localctx: FunctionNameContext = new FunctionNameContext(this, this._ctx, this.state); + this.enterRule(localctx, 24, esql_parser.RULE_functionName); + try { + this.state = 289; + this._errHandler.sync(this); + switch ( this._interp.adaptivePredict(this._input, 20, this._ctx) ) { + case 1: + this.enterOuterAlt(localctx, 1); + { + this.state = 287; + this.match(esql_parser.MATCH); + } + break; + case 2: + this.enterOuterAlt(localctx, 2); + { + this.state = 288; + this.identifierOrParameter(); + } + break; + } + } + catch (re) { + if (re instanceof RecognitionException) { + localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return localctx; + } + // @RuleVersion(0) public dataType(): DataTypeContext { let localctx: DataTypeContext = new DataTypeContext(this, this._ctx, this.state); - this.enterRule(localctx, 24, esql_parser.RULE_dataType); + this.enterRule(localctx, 26, esql_parser.RULE_dataType); try { localctx = new ToDataTypeContext(this, localctx); this.enterOuterAlt(localctx, 1); { - this.state = 281; + this.state = 291; this.identifier(); } } @@ -1380,13 +1420,13 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public rowCommand(): RowCommandContext { let localctx: RowCommandContext = new RowCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 26, esql_parser.RULE_rowCommand); + this.enterRule(localctx, 28, esql_parser.RULE_rowCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 283; + this.state = 293; this.match(esql_parser.ROW); - this.state = 284; + this.state = 294; this.fields(); } } @@ -1407,30 +1447,30 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public fields(): FieldsContext { let localctx: FieldsContext = new FieldsContext(this, this._ctx, this.state); - this.enterRule(localctx, 28, esql_parser.RULE_fields); + this.enterRule(localctx, 30, esql_parser.RULE_fields); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 286; + this.state = 296; this.field(); - this.state = 291; + this.state = 301; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 20, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 21, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 287; + this.state = 297; this.match(esql_parser.COMMA); - this.state = 288; + this.state = 298; this.field(); } } } - this.state = 293; + this.state = 303; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 20, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 21, this._ctx); } } } @@ -1451,30 +1491,25 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public field(): FieldContext { let localctx: FieldContext = new FieldContext(this, this._ctx, this.state); - this.enterRule(localctx, 30, esql_parser.RULE_field); + this.enterRule(localctx, 32, esql_parser.RULE_field); try { - this.state = 299; + this.enterOuterAlt(localctx, 1); + { + this.state = 307; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 21, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 22, this._ctx) ) { case 1: - this.enterOuterAlt(localctx, 1); - { - this.state = 294; - this.booleanExpression(0); - } - break; - case 2: - this.enterOuterAlt(localctx, 2); { - this.state = 295; + this.state = 304; this.qualifiedName(); - this.state = 296; + this.state = 305; this.match(esql_parser.ASSIGN); - this.state = 297; - this.booleanExpression(0); } break; } + this.state = 309; + this.booleanExpression(0); + } } catch (re) { if (re instanceof RecognitionException) { @@ -1493,39 +1528,39 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public fromCommand(): FromCommandContext { let localctx: FromCommandContext = new FromCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 32, esql_parser.RULE_fromCommand); + this.enterRule(localctx, 34, esql_parser.RULE_fromCommand); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 301; + this.state = 311; this.match(esql_parser.FROM); - this.state = 302; + this.state = 312; this.indexPattern(); - this.state = 307; + this.state = 317; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 22, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 23, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 303; + this.state = 313; this.match(esql_parser.COMMA); - this.state = 304; + this.state = 314; this.indexPattern(); } } } - this.state = 309; + this.state = 319; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 22, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 23, this._ctx); } - this.state = 311; + this.state = 321; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 23, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 24, this._ctx) ) { case 1: { - this.state = 310; + this.state = 320; this.metadata(); } break; @@ -1549,30 +1584,25 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public indexPattern(): IndexPatternContext { let localctx: IndexPatternContext = new IndexPatternContext(this, this._ctx, this.state); - this.enterRule(localctx, 34, esql_parser.RULE_indexPattern); + this.enterRule(localctx, 36, esql_parser.RULE_indexPattern); try { - this.state = 318; + this.enterOuterAlt(localctx, 1); + { + this.state = 326; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 24, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 25, this._ctx) ) { case 1: - this.enterOuterAlt(localctx, 1); { - this.state = 313; + this.state = 323; this.clusterString(); - this.state = 314; + this.state = 324; this.match(esql_parser.COLON); - this.state = 315; - this.indexString(); - } - break; - case 2: - this.enterOuterAlt(localctx, 2); - { - this.state = 317; - this.indexString(); } break; } + this.state = 328; + this.indexString(); + } } catch (re) { if (re instanceof RecognitionException) { @@ -1591,11 +1621,11 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public clusterString(): ClusterStringContext { let localctx: ClusterStringContext = new ClusterStringContext(this, this._ctx, this.state); - this.enterRule(localctx, 36, esql_parser.RULE_clusterString); + this.enterRule(localctx, 38, esql_parser.RULE_clusterString); try { this.enterOuterAlt(localctx, 1); { - this.state = 320; + this.state = 330; this.match(esql_parser.UNQUOTED_SOURCE); } } @@ -1616,14 +1646,14 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public indexString(): IndexStringContext { let localctx: IndexStringContext = new IndexStringContext(this, this._ctx, this.state); - this.enterRule(localctx, 38, esql_parser.RULE_indexString); + this.enterRule(localctx, 40, esql_parser.RULE_indexString); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 322; + this.state = 332; _la = this._input.LA(1); - if(!(_la===26 || _la===76)) { + if(!(_la===25 || _la===76)) { this._errHandler.recoverInline(this); } else { @@ -1649,22 +1679,22 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public metadata(): MetadataContext { let localctx: MetadataContext = new MetadataContext(this, this._ctx, this.state); - this.enterRule(localctx, 40, esql_parser.RULE_metadata); + this.enterRule(localctx, 42, esql_parser.RULE_metadata); try { - this.state = 326; + this.state = 336; this._errHandler.sync(this); switch (this._input.LA(1)) { case 75: this.enterOuterAlt(localctx, 1); { - this.state = 324; + this.state = 334; this.metadataOption(); } break; case 65: this.enterOuterAlt(localctx, 2); { - this.state = 325; + this.state = 335; this.deprecated_metadata(); } break; @@ -1689,32 +1719,32 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public metadataOption(): MetadataOptionContext { let localctx: MetadataOptionContext = new MetadataOptionContext(this, this._ctx, this.state); - this.enterRule(localctx, 42, esql_parser.RULE_metadataOption); + this.enterRule(localctx, 44, esql_parser.RULE_metadataOption); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 328; + this.state = 338; this.match(esql_parser.METADATA); - this.state = 329; + this.state = 339; this.match(esql_parser.UNQUOTED_SOURCE); - this.state = 334; + this.state = 344; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 26, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 27, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 330; + this.state = 340; this.match(esql_parser.COMMA); - this.state = 331; + this.state = 341; this.match(esql_parser.UNQUOTED_SOURCE); } } } - this.state = 336; + this.state = 346; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 26, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 27, this._ctx); } } } @@ -1735,15 +1765,15 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public deprecated_metadata(): Deprecated_metadataContext { let localctx: Deprecated_metadataContext = new Deprecated_metadataContext(this, this._ctx, this.state); - this.enterRule(localctx, 44, esql_parser.RULE_deprecated_metadata); + this.enterRule(localctx, 46, esql_parser.RULE_deprecated_metadata); try { this.enterOuterAlt(localctx, 1); { - this.state = 337; + this.state = 347; this.match(esql_parser.OPENING_BRACKET); - this.state = 338; + this.state = 348; this.metadataOption(); - this.state = 339; + this.state = 349; this.match(esql_parser.CLOSING_BRACKET); } } @@ -1764,51 +1794,51 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public metricsCommand(): MetricsCommandContext { let localctx: MetricsCommandContext = new MetricsCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 46, esql_parser.RULE_metricsCommand); + this.enterRule(localctx, 48, esql_parser.RULE_metricsCommand); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 341; + this.state = 351; this.match(esql_parser.DEV_METRICS); - this.state = 342; + this.state = 352; this.indexPattern(); - this.state = 347; + this.state = 357; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 27, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 28, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 343; + this.state = 353; this.match(esql_parser.COMMA); - this.state = 344; + this.state = 354; this.indexPattern(); } } } - this.state = 349; + this.state = 359; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 27, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 28, this._ctx); } - this.state = 351; + this.state = 361; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 28, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 29, this._ctx) ) { case 1: { - this.state = 350; - localctx._aggregates = this.fields(); + this.state = 360; + localctx._aggregates = this.aggFields(); } break; } - this.state = 355; + this.state = 365; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 29, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 30, this._ctx) ) { case 1: { - this.state = 353; + this.state = 363; this.match(esql_parser.BY); - this.state = 354; + this.state = 364; localctx._grouping = this.fields(); } break; @@ -1832,13 +1862,13 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public evalCommand(): EvalCommandContext { let localctx: EvalCommandContext = new EvalCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 48, esql_parser.RULE_evalCommand); + this.enterRule(localctx, 50, esql_parser.RULE_evalCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 357; + this.state = 367; this.match(esql_parser.EVAL); - this.state = 358; + this.state = 368; this.fields(); } } @@ -1859,30 +1889,30 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public statsCommand(): StatsCommandContext { let localctx: StatsCommandContext = new StatsCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 50, esql_parser.RULE_statsCommand); + this.enterRule(localctx, 52, esql_parser.RULE_statsCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 360; + this.state = 370; this.match(esql_parser.STATS); - this.state = 362; + this.state = 372; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 30, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 31, this._ctx) ) { case 1: { - this.state = 361; - localctx._stats = this.fields(); + this.state = 371; + localctx._stats = this.aggFields(); } break; } - this.state = 366; + this.state = 376; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 31, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 32, this._ctx) ) { case 1: { - this.state = 364; + this.state = 374; this.match(esql_parser.BY); - this.state = 365; + this.state = 375; localctx._grouping = this.fields(); } break; @@ -1904,32 +1934,113 @@ export default class esql_parser extends parser_config { return localctx; } // @RuleVersion(0) + public aggFields(): AggFieldsContext { + let localctx: AggFieldsContext = new AggFieldsContext(this, this._ctx, this.state); + this.enterRule(localctx, 54, esql_parser.RULE_aggFields); + try { + let _alt: number; + this.enterOuterAlt(localctx, 1); + { + this.state = 378; + this.aggField(); + this.state = 383; + this._errHandler.sync(this); + _alt = this._interp.adaptivePredict(this._input, 33, this._ctx); + while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { + if (_alt === 1) { + { + { + this.state = 379; + this.match(esql_parser.COMMA); + this.state = 380; + this.aggField(); + } + } + } + this.state = 385; + this._errHandler.sync(this); + _alt = this._interp.adaptivePredict(this._input, 33, this._ctx); + } + } + } + catch (re) { + if (re instanceof RecognitionException) { + localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return localctx; + } + // @RuleVersion(0) + public aggField(): AggFieldContext { + let localctx: AggFieldContext = new AggFieldContext(this, this._ctx, this.state); + this.enterRule(localctx, 56, esql_parser.RULE_aggField); + try { + this.enterOuterAlt(localctx, 1); + { + this.state = 386; + this.field(); + this.state = 389; + this._errHandler.sync(this); + switch ( this._interp.adaptivePredict(this._input, 34, this._ctx) ) { + case 1: + { + this.state = 387; + this.match(esql_parser.WHERE); + this.state = 388; + this.booleanExpression(0); + } + break; + } + } + } + catch (re) { + if (re instanceof RecognitionException) { + localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return localctx; + } + // @RuleVersion(0) public qualifiedName(): QualifiedNameContext { let localctx: QualifiedNameContext = new QualifiedNameContext(this, this._ctx, this.state); - this.enterRule(localctx, 52, esql_parser.RULE_qualifiedName); + this.enterRule(localctx, 58, esql_parser.RULE_qualifiedName); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 368; + this.state = 391; this.identifierOrParameter(); - this.state = 373; + this.state = 396; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 32, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 35, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 369; + this.state = 392; this.match(esql_parser.DOT); - this.state = 370; + this.state = 393; this.identifierOrParameter(); } } } - this.state = 375; + this.state = 398; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 32, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 35, this._ctx); } } } @@ -1950,30 +2061,30 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public qualifiedNamePattern(): QualifiedNamePatternContext { let localctx: QualifiedNamePatternContext = new QualifiedNamePatternContext(this, this._ctx, this.state); - this.enterRule(localctx, 54, esql_parser.RULE_qualifiedNamePattern); + this.enterRule(localctx, 60, esql_parser.RULE_qualifiedNamePattern); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 376; + this.state = 399; this.identifierPattern(); - this.state = 381; + this.state = 404; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 33, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 36, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 377; + this.state = 400; this.match(esql_parser.DOT); - this.state = 378; + this.state = 401; this.identifierPattern(); } } } - this.state = 383; + this.state = 406; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 33, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 36, this._ctx); } } } @@ -1994,30 +2105,30 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public qualifiedNamePatterns(): QualifiedNamePatternsContext { let localctx: QualifiedNamePatternsContext = new QualifiedNamePatternsContext(this, this._ctx, this.state); - this.enterRule(localctx, 56, esql_parser.RULE_qualifiedNamePatterns); + this.enterRule(localctx, 62, esql_parser.RULE_qualifiedNamePatterns); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 384; + this.state = 407; this.qualifiedNamePattern(); - this.state = 389; + this.state = 412; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 34, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 37, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 385; + this.state = 408; this.match(esql_parser.COMMA); - this.state = 386; + this.state = 409; this.qualifiedNamePattern(); } } } - this.state = 391; + this.state = 414; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 34, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 37, this._ctx); } } } @@ -2038,12 +2149,12 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public identifier(): IdentifierContext { let localctx: IdentifierContext = new IdentifierContext(this, this._ctx, this.state); - this.enterRule(localctx, 58, esql_parser.RULE_identifier); + this.enterRule(localctx, 64, esql_parser.RULE_identifier); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 392; + this.state = 415; _la = this._input.LA(1); if(!(_la===67 || _la===68)) { this._errHandler.recoverInline(this); @@ -2071,28 +2182,29 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public identifierPattern(): IdentifierPatternContext { let localctx: IdentifierPatternContext = new IdentifierPatternContext(this, this._ctx, this.state); - this.enterRule(localctx, 60, esql_parser.RULE_identifierPattern); + this.enterRule(localctx, 66, esql_parser.RULE_identifierPattern); try { - this.state = 396; + this.state = 420; this._errHandler.sync(this); - switch (this._input.LA(1)) { - case 80: + switch ( this._interp.adaptivePredict(this._input, 38, this._ctx) ) { + case 1: this.enterOuterAlt(localctx, 1); { - this.state = 394; + this.state = 417; this.match(esql_parser.ID_PATTERN); } break; - case 48: - case 64: + case 2: this.enterOuterAlt(localctx, 2); { - this.state = 395; + this.state = 418; + if (!(this.isDevVersion())) { + throw this.createFailedPredicateException("this.isDevVersion()"); + } + this.state = 419; this.parameter(); } break; - default: - throw new NoViableAltException(this); } } catch (re) { @@ -2112,17 +2224,17 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public constant(): ConstantContext { let localctx: ConstantContext = new ConstantContext(this, this._ctx, this.state); - this.enterRule(localctx, 62, esql_parser.RULE_constant); + this.enterRule(localctx, 68, esql_parser.RULE_constant); let _la: number; try { - this.state = 440; + this.state = 464; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 39, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 42, this._ctx) ) { case 1: localctx = new NullLiteralContext(this, localctx); this.enterOuterAlt(localctx, 1); { - this.state = 398; + this.state = 422; this.match(esql_parser.NULL); } break; @@ -2130,9 +2242,9 @@ export default class esql_parser extends parser_config { localctx = new QualifiedIntegerLiteralContext(this, localctx); this.enterOuterAlt(localctx, 2); { - this.state = 399; + this.state = 423; this.integerValue(); - this.state = 400; + this.state = 424; this.match(esql_parser.UNQUOTED_IDENTIFIER); } break; @@ -2140,7 +2252,7 @@ export default class esql_parser extends parser_config { localctx = new DecimalLiteralContext(this, localctx); this.enterOuterAlt(localctx, 3); { - this.state = 402; + this.state = 426; this.decimalValue(); } break; @@ -2148,7 +2260,7 @@ export default class esql_parser extends parser_config { localctx = new IntegerLiteralContext(this, localctx); this.enterOuterAlt(localctx, 4); { - this.state = 403; + this.state = 427; this.integerValue(); } break; @@ -2156,7 +2268,7 @@ export default class esql_parser extends parser_config { localctx = new BooleanLiteralContext(this, localctx); this.enterOuterAlt(localctx, 5); { - this.state = 404; + this.state = 428; this.booleanValue(); } break; @@ -2164,7 +2276,7 @@ export default class esql_parser extends parser_config { localctx = new InputParameterContext(this, localctx); this.enterOuterAlt(localctx, 6); { - this.state = 405; + this.state = 429; this.parameter(); } break; @@ -2172,7 +2284,7 @@ export default class esql_parser extends parser_config { localctx = new StringLiteralContext(this, localctx); this.enterOuterAlt(localctx, 7); { - this.state = 406; + this.state = 430; this.string_(); } break; @@ -2180,27 +2292,27 @@ export default class esql_parser extends parser_config { localctx = new NumericArrayLiteralContext(this, localctx); this.enterOuterAlt(localctx, 8); { - this.state = 407; + this.state = 431; this.match(esql_parser.OPENING_BRACKET); - this.state = 408; + this.state = 432; this.numericValue(); - this.state = 413; + this.state = 437; this._errHandler.sync(this); _la = this._input.LA(1); - while (_la===34) { + while (_la===33) { { { - this.state = 409; + this.state = 433; this.match(esql_parser.COMMA); - this.state = 410; + this.state = 434; this.numericValue(); } } - this.state = 415; + this.state = 439; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 416; + this.state = 440; this.match(esql_parser.CLOSING_BRACKET); } break; @@ -2208,27 +2320,27 @@ export default class esql_parser extends parser_config { localctx = new BooleanArrayLiteralContext(this, localctx); this.enterOuterAlt(localctx, 9); { - this.state = 418; + this.state = 442; this.match(esql_parser.OPENING_BRACKET); - this.state = 419; + this.state = 443; this.booleanValue(); - this.state = 424; + this.state = 448; this._errHandler.sync(this); _la = this._input.LA(1); - while (_la===34) { + while (_la===33) { { { - this.state = 420; + this.state = 444; this.match(esql_parser.COMMA); - this.state = 421; + this.state = 445; this.booleanValue(); } } - this.state = 426; + this.state = 450; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 427; + this.state = 451; this.match(esql_parser.CLOSING_BRACKET); } break; @@ -2236,27 +2348,27 @@ export default class esql_parser extends parser_config { localctx = new StringArrayLiteralContext(this, localctx); this.enterOuterAlt(localctx, 10); { - this.state = 429; + this.state = 453; this.match(esql_parser.OPENING_BRACKET); - this.state = 430; + this.state = 454; this.string_(); - this.state = 435; + this.state = 459; this._errHandler.sync(this); _la = this._input.LA(1); - while (_la===34) { + while (_la===33) { { { - this.state = 431; + this.state = 455; this.match(esql_parser.COMMA); - this.state = 432; + this.state = 456; this.string_(); } } - this.state = 437; + this.state = 461; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 438; + this.state = 462; this.match(esql_parser.CLOSING_BRACKET); } break; @@ -2279,16 +2391,16 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public parameter(): ParameterContext { let localctx: ParameterContext = new ParameterContext(this, this._ctx, this.state); - this.enterRule(localctx, 64, esql_parser.RULE_parameter); + this.enterRule(localctx, 70, esql_parser.RULE_parameter); try { - this.state = 444; + this.state = 468; this._errHandler.sync(this); switch (this._input.LA(1)) { - case 48: + case 47: localctx = new InputParamContext(this, localctx); this.enterOuterAlt(localctx, 1); { - this.state = 442; + this.state = 466; this.match(esql_parser.PARAM); } break; @@ -2296,7 +2408,7 @@ export default class esql_parser extends parser_config { localctx = new InputNamedOrPositionalParamContext(this, localctx); this.enterOuterAlt(localctx, 2); { - this.state = 443; + this.state = 467; this.match(esql_parser.NAMED_OR_POSITIONAL_PARAM); } break; @@ -2321,29 +2433,29 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public identifierOrParameter(): IdentifierOrParameterContext { let localctx: IdentifierOrParameterContext = new IdentifierOrParameterContext(this, this._ctx, this.state); - this.enterRule(localctx, 66, esql_parser.RULE_identifierOrParameter); + this.enterRule(localctx, 72, esql_parser.RULE_identifierOrParameter); try { - this.state = 448; + this.state = 473; this._errHandler.sync(this); - switch (this._input.LA(1)) { - case 67: - case 68: + switch ( this._interp.adaptivePredict(this._input, 44, this._ctx) ) { + case 1: this.enterOuterAlt(localctx, 1); { - this.state = 446; + this.state = 470; this.identifier(); } break; - case 48: - case 64: + case 2: this.enterOuterAlt(localctx, 2); { - this.state = 447; + this.state = 471; + if (!(this.isDevVersion())) { + throw this.createFailedPredicateException("this.isDevVersion()"); + } + this.state = 472; this.parameter(); } break; - default: - throw new NoViableAltException(this); } } catch (re) { @@ -2363,13 +2475,13 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public limitCommand(): LimitCommandContext { let localctx: LimitCommandContext = new LimitCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 68, esql_parser.RULE_limitCommand); + this.enterRule(localctx, 74, esql_parser.RULE_limitCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 450; + this.state = 475; this.match(esql_parser.LIMIT); - this.state = 451; + this.state = 476; this.match(esql_parser.INTEGER_LITERAL); } } @@ -2390,32 +2502,32 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public sortCommand(): SortCommandContext { let localctx: SortCommandContext = new SortCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 70, esql_parser.RULE_sortCommand); + this.enterRule(localctx, 76, esql_parser.RULE_sortCommand); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 453; + this.state = 478; this.match(esql_parser.SORT); - this.state = 454; + this.state = 479; this.orderExpression(); - this.state = 459; + this.state = 484; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 42, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 45, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 455; + this.state = 480; this.match(esql_parser.COMMA); - this.state = 456; + this.state = 481; this.orderExpression(); } } } - this.state = 461; + this.state = 486; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 42, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 45, this._ctx); } } } @@ -2436,22 +2548,22 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public orderExpression(): OrderExpressionContext { let localctx: OrderExpressionContext = new OrderExpressionContext(this, this._ctx, this.state); - this.enterRule(localctx, 72, esql_parser.RULE_orderExpression); + this.enterRule(localctx, 78, esql_parser.RULE_orderExpression); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 462; + this.state = 487; this.booleanExpression(0); - this.state = 464; + this.state = 489; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 43, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 46, this._ctx) ) { case 1: { - this.state = 463; + this.state = 488; localctx._ordering = this._input.LT(1); _la = this._input.LA(1); - if(!(_la===31 || _la===35)) { + if(!(_la===30 || _la===34)) { localctx._ordering = this._errHandler.recoverInline(this); } else { @@ -2461,17 +2573,17 @@ export default class esql_parser extends parser_config { } break; } - this.state = 468; + this.state = 493; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 44, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 47, this._ctx) ) { case 1: { - this.state = 466; + this.state = 491; this.match(esql_parser.NULLS); - this.state = 467; + this.state = 492; localctx._nullOrdering = this._input.LT(1); _la = this._input.LA(1); - if(!(_la===38 || _la===41)) { + if(!(_la===37 || _la===40)) { localctx._nullOrdering = this._errHandler.recoverInline(this); } else { @@ -2500,13 +2612,13 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public keepCommand(): KeepCommandContext { let localctx: KeepCommandContext = new KeepCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 74, esql_parser.RULE_keepCommand); + this.enterRule(localctx, 80, esql_parser.RULE_keepCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 470; + this.state = 495; this.match(esql_parser.KEEP); - this.state = 471; + this.state = 496; this.qualifiedNamePatterns(); } } @@ -2527,13 +2639,13 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public dropCommand(): DropCommandContext { let localctx: DropCommandContext = new DropCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 76, esql_parser.RULE_dropCommand); + this.enterRule(localctx, 82, esql_parser.RULE_dropCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 473; + this.state = 498; this.match(esql_parser.DROP); - this.state = 474; + this.state = 499; this.qualifiedNamePatterns(); } } @@ -2554,32 +2666,32 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public renameCommand(): RenameCommandContext { let localctx: RenameCommandContext = new RenameCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 78, esql_parser.RULE_renameCommand); + this.enterRule(localctx, 84, esql_parser.RULE_renameCommand); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 476; + this.state = 501; this.match(esql_parser.RENAME); - this.state = 477; + this.state = 502; this.renameClause(); - this.state = 482; + this.state = 507; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 45, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 48, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 478; + this.state = 503; this.match(esql_parser.COMMA); - this.state = 479; + this.state = 504; this.renameClause(); } } } - this.state = 484; + this.state = 509; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 45, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 48, this._ctx); } } } @@ -2600,15 +2712,15 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public renameClause(): RenameClauseContext { let localctx: RenameClauseContext = new RenameClauseContext(this, this._ctx, this.state); - this.enterRule(localctx, 80, esql_parser.RULE_renameClause); + this.enterRule(localctx, 86, esql_parser.RULE_renameClause); try { this.enterOuterAlt(localctx, 1); { - this.state = 485; + this.state = 510; localctx._oldName = this.qualifiedNamePattern(); - this.state = 486; + this.state = 511; this.match(esql_parser.AS); - this.state = 487; + this.state = 512; localctx._newName = this.qualifiedNamePattern(); } } @@ -2629,22 +2741,22 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public dissectCommand(): DissectCommandContext { let localctx: DissectCommandContext = new DissectCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 82, esql_parser.RULE_dissectCommand); + this.enterRule(localctx, 88, esql_parser.RULE_dissectCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 489; + this.state = 514; this.match(esql_parser.DISSECT); - this.state = 490; + this.state = 515; this.primaryExpression(0); - this.state = 491; + this.state = 516; this.string_(); - this.state = 493; + this.state = 518; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 46, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 49, this._ctx) ) { case 1: { - this.state = 492; + this.state = 517; this.commandOptions(); } break; @@ -2668,15 +2780,15 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public grokCommand(): GrokCommandContext { let localctx: GrokCommandContext = new GrokCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 84, esql_parser.RULE_grokCommand); + this.enterRule(localctx, 90, esql_parser.RULE_grokCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 495; + this.state = 520; this.match(esql_parser.GROK); - this.state = 496; + this.state = 521; this.primaryExpression(0); - this.state = 497; + this.state = 522; this.string_(); } } @@ -2697,13 +2809,13 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public mvExpandCommand(): MvExpandCommandContext { let localctx: MvExpandCommandContext = new MvExpandCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 86, esql_parser.RULE_mvExpandCommand); + this.enterRule(localctx, 92, esql_parser.RULE_mvExpandCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 499; + this.state = 524; this.match(esql_parser.MV_EXPAND); - this.state = 500; + this.state = 525; this.qualifiedName(); } } @@ -2724,30 +2836,30 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public commandOptions(): CommandOptionsContext { let localctx: CommandOptionsContext = new CommandOptionsContext(this, this._ctx, this.state); - this.enterRule(localctx, 88, esql_parser.RULE_commandOptions); + this.enterRule(localctx, 94, esql_parser.RULE_commandOptions); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 502; + this.state = 527; this.commandOption(); - this.state = 507; + this.state = 532; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 47, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 50, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 503; + this.state = 528; this.match(esql_parser.COMMA); - this.state = 504; + this.state = 529; this.commandOption(); } } } - this.state = 509; + this.state = 534; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 47, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 50, this._ctx); } } } @@ -2768,15 +2880,15 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public commandOption(): CommandOptionContext { let localctx: CommandOptionContext = new CommandOptionContext(this, this._ctx, this.state); - this.enterRule(localctx, 90, esql_parser.RULE_commandOption); + this.enterRule(localctx, 96, esql_parser.RULE_commandOption); try { this.enterOuterAlt(localctx, 1); { - this.state = 510; + this.state = 535; this.identifier(); - this.state = 511; + this.state = 536; this.match(esql_parser.ASSIGN); - this.state = 512; + this.state = 537; this.constant(); } } @@ -2797,14 +2909,14 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public booleanValue(): BooleanValueContext { let localctx: BooleanValueContext = new BooleanValueContext(this, this._ctx, this.state); - this.enterRule(localctx, 92, esql_parser.RULE_booleanValue); + this.enterRule(localctx, 98, esql_parser.RULE_booleanValue); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 514; + this.state = 539; _la = this._input.LA(1); - if(!(_la===37 || _la===51)) { + if(!(_la===36 || _la===50)) { this._errHandler.recoverInline(this); } else { @@ -2830,22 +2942,22 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public numericValue(): NumericValueContext { let localctx: NumericValueContext = new NumericValueContext(this, this._ctx, this.state); - this.enterRule(localctx, 94, esql_parser.RULE_numericValue); + this.enterRule(localctx, 100, esql_parser.RULE_numericValue); try { - this.state = 518; + this.state = 543; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 48, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 51, this._ctx) ) { case 1: this.enterOuterAlt(localctx, 1); { - this.state = 516; + this.state = 541; this.decimalValue(); } break; case 2: this.enterOuterAlt(localctx, 2); { - this.state = 517; + this.state = 542; this.integerValue(); } break; @@ -2868,19 +2980,19 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public decimalValue(): DecimalValueContext { let localctx: DecimalValueContext = new DecimalValueContext(this, this._ctx, this.state); - this.enterRule(localctx, 96, esql_parser.RULE_decimalValue); + this.enterRule(localctx, 102, esql_parser.RULE_decimalValue); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 521; + this.state = 546; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===59 || _la===60) { + if (_la===58 || _la===59) { { - this.state = 520; + this.state = 545; _la = this._input.LA(1); - if(!(_la===59 || _la===60)) { + if(!(_la===58 || _la===59)) { this._errHandler.recoverInline(this); } else { @@ -2890,7 +3002,7 @@ export default class esql_parser extends parser_config { } } - this.state = 523; + this.state = 548; this.match(esql_parser.DECIMAL_LITERAL); } } @@ -2911,19 +3023,19 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public integerValue(): IntegerValueContext { let localctx: IntegerValueContext = new IntegerValueContext(this, this._ctx, this.state); - this.enterRule(localctx, 98, esql_parser.RULE_integerValue); + this.enterRule(localctx, 104, esql_parser.RULE_integerValue); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 526; + this.state = 551; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===59 || _la===60) { + if (_la===58 || _la===59) { { - this.state = 525; + this.state = 550; _la = this._input.LA(1); - if(!(_la===59 || _la===60)) { + if(!(_la===58 || _la===59)) { this._errHandler.recoverInline(this); } else { @@ -2933,7 +3045,7 @@ export default class esql_parser extends parser_config { } } - this.state = 528; + this.state = 553; this.match(esql_parser.INTEGER_LITERAL); } } @@ -2954,11 +3066,11 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public string_(): StringContext { let localctx: StringContext = new StringContext(this, this._ctx, this.state); - this.enterRule(localctx, 100, esql_parser.RULE_string); + this.enterRule(localctx, 106, esql_parser.RULE_string); try { this.enterOuterAlt(localctx, 1); { - this.state = 530; + this.state = 555; this.match(esql_parser.QUOTED_STRING); } } @@ -2979,14 +3091,14 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public comparisonOperator(): ComparisonOperatorContext { let localctx: ComparisonOperatorContext = new ComparisonOperatorContext(this, this._ctx, this.state); - this.enterRule(localctx, 102, esql_parser.RULE_comparisonOperator); + this.enterRule(localctx, 108, esql_parser.RULE_comparisonOperator); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 532; + this.state = 557; _la = this._input.LA(1); - if(!(((((_la - 52)) & ~0x1F) === 0 && ((1 << (_la - 52)) & 125) !== 0))) { + if(!(((((_la - 51)) & ~0x1F) === 0 && ((1 << (_la - 51)) & 125) !== 0))) { this._errHandler.recoverInline(this); } else { @@ -3012,13 +3124,13 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public explainCommand(): ExplainCommandContext { let localctx: ExplainCommandContext = new ExplainCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 104, esql_parser.RULE_explainCommand); + this.enterRule(localctx, 110, esql_parser.RULE_explainCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 534; + this.state = 559; this.match(esql_parser.EXPLAIN); - this.state = 535; + this.state = 560; this.subqueryExpression(); } } @@ -3039,15 +3151,15 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public subqueryExpression(): SubqueryExpressionContext { let localctx: SubqueryExpressionContext = new SubqueryExpressionContext(this, this._ctx, this.state); - this.enterRule(localctx, 106, esql_parser.RULE_subqueryExpression); + this.enterRule(localctx, 112, esql_parser.RULE_subqueryExpression); try { this.enterOuterAlt(localctx, 1); { - this.state = 537; + this.state = 562; this.match(esql_parser.OPENING_BRACKET); - this.state = 538; + this.state = 563; this.query(0); - this.state = 539; + this.state = 564; this.match(esql_parser.CLOSING_BRACKET); } } @@ -3068,14 +3180,14 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public showCommand(): ShowCommandContext { let localctx: ShowCommandContext = new ShowCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 108, esql_parser.RULE_showCommand); + this.enterRule(localctx, 114, esql_parser.RULE_showCommand); try { localctx = new ShowInfoContext(this, localctx); this.enterOuterAlt(localctx, 1); { - this.state = 541; + this.state = 566; this.match(esql_parser.SHOW); - this.state = 542; + this.state = 567; this.match(esql_parser.INFO); } } @@ -3096,53 +3208,53 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public enrichCommand(): EnrichCommandContext { let localctx: EnrichCommandContext = new EnrichCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 110, esql_parser.RULE_enrichCommand); + this.enterRule(localctx, 116, esql_parser.RULE_enrichCommand); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 544; + this.state = 569; this.match(esql_parser.ENRICH); - this.state = 545; + this.state = 570; localctx._policyName = this.match(esql_parser.ENRICH_POLICY_NAME); - this.state = 548; + this.state = 573; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 51, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 54, this._ctx) ) { case 1: { - this.state = 546; + this.state = 571; this.match(esql_parser.ON); - this.state = 547; + this.state = 572; localctx._matchField = this.qualifiedNamePattern(); } break; } - this.state = 559; + this.state = 584; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 53, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 56, this._ctx) ) { case 1: { - this.state = 550; + this.state = 575; this.match(esql_parser.WITH); - this.state = 551; + this.state = 576; this.enrichWithClause(); - this.state = 556; + this.state = 581; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 52, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 55, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 552; + this.state = 577; this.match(esql_parser.COMMA); - this.state = 553; + this.state = 578; this.enrichWithClause(); } } } - this.state = 558; + this.state = 583; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 52, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 55, this._ctx); } } break; @@ -3166,23 +3278,23 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public enrichWithClause(): EnrichWithClauseContext { let localctx: EnrichWithClauseContext = new EnrichWithClauseContext(this, this._ctx, this.state); - this.enterRule(localctx, 112, esql_parser.RULE_enrichWithClause); + this.enterRule(localctx, 118, esql_parser.RULE_enrichWithClause); try { this.enterOuterAlt(localctx, 1); { - this.state = 564; + this.state = 589; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 54, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 57, this._ctx) ) { case 1: { - this.state = 561; + this.state = 586; localctx._newName = this.qualifiedNamePattern(); - this.state = 562; + this.state = 587; this.match(esql_parser.ASSIGN); } break; } - this.state = 566; + this.state = 591; localctx._enrichField = this.qualifiedNamePattern(); } } @@ -3203,17 +3315,17 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public lookupCommand(): LookupCommandContext { let localctx: LookupCommandContext = new LookupCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 114, esql_parser.RULE_lookupCommand); + this.enterRule(localctx, 120, esql_parser.RULE_lookupCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 568; + this.state = 593; this.match(esql_parser.DEV_LOOKUP); - this.state = 569; + this.state = 594; localctx._tableName = this.indexPattern(); - this.state = 570; + this.state = 595; this.match(esql_parser.ON); - this.state = 571; + this.state = 596; localctx._matchFields = this.qualifiedNamePatterns(); } } @@ -3234,22 +3346,22 @@ export default class esql_parser extends parser_config { // @RuleVersion(0) public inlinestatsCommand(): InlinestatsCommandContext { let localctx: InlinestatsCommandContext = new InlinestatsCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 116, esql_parser.RULE_inlinestatsCommand); + this.enterRule(localctx, 122, esql_parser.RULE_inlinestatsCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 573; + this.state = 598; this.match(esql_parser.DEV_INLINESTATS); - this.state = 574; - localctx._stats = this.fields(); - this.state = 577; + this.state = 599; + localctx._stats = this.aggFields(); + this.state = 602; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 55, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 58, this._ctx) ) { case 1: { - this.state = 575; + this.state = 600; this.match(esql_parser.BY); - this.state = 576; + this.state = 601; localctx._grouping = this.fields(); } break; @@ -3285,6 +3397,10 @@ export default class esql_parser extends parser_config { return this.operatorExpression_sempred(localctx as OperatorExpressionContext, predIndex); case 10: return this.primaryExpression_sempred(localctx as PrimaryExpressionContext, predIndex); + case 33: + return this.identifierPattern_sempred(localctx as IdentifierPatternContext, predIndex); + case 36: + return this.identifierOrParameter_sempred(localctx as IdentifierOrParameterContext, predIndex); } return true; } @@ -3338,8 +3454,22 @@ export default class esql_parser extends parser_config { } return true; } + private identifierPattern_sempred(localctx: IdentifierPatternContext, predIndex: number): boolean { + switch (predIndex) { + case 10: + return this.isDevVersion(); + } + return true; + } + private identifierOrParameter_sempred(localctx: IdentifierOrParameterContext, predIndex: number): boolean { + switch (predIndex) { + case 11: + return this.isDevVersion(); + } + return true; + } - public static readonly _serializedATN: number[] = [4,1,120,580,2,0,7,0, + public static readonly _serializedATN: number[] = [4,1,120,605,2,0,7,0, 2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,6,2,7,7,7,2,8,7,8,2,9,7,9, 2,10,7,10,2,11,7,11,2,12,7,12,2,13,7,13,2,14,7,14,2,15,7,15,2,16,7,16,2, 17,7,17,2,18,7,18,2,19,7,19,2,20,7,20,2,21,7,21,2,22,7,22,2,23,7,23,2,24, @@ -3347,188 +3477,196 @@ export default class esql_parser extends parser_config { 31,2,32,7,32,2,33,7,33,2,34,7,34,2,35,7,35,2,36,7,36,2,37,7,37,2,38,7,38, 2,39,7,39,2,40,7,40,2,41,7,41,2,42,7,42,2,43,7,43,2,44,7,44,2,45,7,45,2, 46,7,46,2,47,7,47,2,48,7,48,2,49,7,49,2,50,7,50,2,51,7,51,2,52,7,52,2,53, - 7,53,2,54,7,54,2,55,7,55,2,56,7,56,2,57,7,57,2,58,7,58,1,0,1,0,1,0,1,1, - 1,1,1,1,1,1,1,1,1,1,5,1,128,8,1,10,1,12,1,131,9,1,1,2,1,2,1,2,1,2,1,2,1, - 2,3,2,139,8,2,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1, - 3,1,3,3,3,157,8,3,1,4,1,4,1,4,1,5,1,5,1,5,1,5,1,5,1,5,1,5,3,5,169,8,5,1, - 5,1,5,1,5,1,5,1,5,5,5,176,8,5,10,5,12,5,179,9,5,1,5,1,5,1,5,1,5,1,5,3,5, - 186,8,5,1,5,1,5,1,5,1,5,3,5,192,8,5,1,5,1,5,1,5,1,5,1,5,1,5,5,5,200,8,5, - 10,5,12,5,203,9,5,1,6,1,6,3,6,207,8,6,1,6,1,6,1,6,1,6,1,6,3,6,214,8,6,1, - 6,1,6,1,6,3,6,219,8,6,1,7,1,7,1,7,1,7,1,8,1,8,1,8,1,8,1,8,3,8,230,8,8,1, - 9,1,9,1,9,1,9,3,9,236,8,9,1,9,1,9,1,9,1,9,1,9,1,9,5,9,244,8,9,10,9,12,9, - 247,9,9,1,10,1,10,1,10,1,10,1,10,1,10,1,10,1,10,3,10,257,8,10,1,10,1,10, - 1,10,5,10,262,8,10,10,10,12,10,265,9,10,1,11,1,11,1,11,1,11,1,11,1,11,5, - 11,273,8,11,10,11,12,11,276,9,11,3,11,278,8,11,1,11,1,11,1,12,1,12,1,13, - 1,13,1,13,1,14,1,14,1,14,5,14,290,8,14,10,14,12,14,293,9,14,1,15,1,15,1, - 15,1,15,1,15,3,15,300,8,15,1,16,1,16,1,16,1,16,5,16,306,8,16,10,16,12,16, - 309,9,16,1,16,3,16,312,8,16,1,17,1,17,1,17,1,17,1,17,3,17,319,8,17,1,18, - 1,18,1,19,1,19,1,20,1,20,3,20,327,8,20,1,21,1,21,1,21,1,21,5,21,333,8,21, - 10,21,12,21,336,9,21,1,22,1,22,1,22,1,22,1,23,1,23,1,23,1,23,5,23,346,8, - 23,10,23,12,23,349,9,23,1,23,3,23,352,8,23,1,23,1,23,3,23,356,8,23,1,24, - 1,24,1,24,1,25,1,25,3,25,363,8,25,1,25,1,25,3,25,367,8,25,1,26,1,26,1,26, - 5,26,372,8,26,10,26,12,26,375,9,26,1,27,1,27,1,27,5,27,380,8,27,10,27,12, - 27,383,9,27,1,28,1,28,1,28,5,28,388,8,28,10,28,12,28,391,9,28,1,29,1,29, - 1,30,1,30,3,30,397,8,30,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1, - 31,1,31,1,31,1,31,5,31,412,8,31,10,31,12,31,415,9,31,1,31,1,31,1,31,1,31, - 1,31,1,31,5,31,423,8,31,10,31,12,31,426,9,31,1,31,1,31,1,31,1,31,1,31,1, - 31,5,31,434,8,31,10,31,12,31,437,9,31,1,31,1,31,3,31,441,8,31,1,32,1,32, - 3,32,445,8,32,1,33,1,33,3,33,449,8,33,1,34,1,34,1,34,1,35,1,35,1,35,1,35, - 5,35,458,8,35,10,35,12,35,461,9,35,1,36,1,36,3,36,465,8,36,1,36,1,36,3, - 36,469,8,36,1,37,1,37,1,37,1,38,1,38,1,38,1,39,1,39,1,39,1,39,5,39,481, - 8,39,10,39,12,39,484,9,39,1,40,1,40,1,40,1,40,1,41,1,41,1,41,1,41,3,41, - 494,8,41,1,42,1,42,1,42,1,42,1,43,1,43,1,43,1,44,1,44,1,44,5,44,506,8,44, - 10,44,12,44,509,9,44,1,45,1,45,1,45,1,45,1,46,1,46,1,47,1,47,3,47,519,8, - 47,1,48,3,48,522,8,48,1,48,1,48,1,49,3,49,527,8,49,1,49,1,49,1,50,1,50, - 1,51,1,51,1,52,1,52,1,52,1,53,1,53,1,53,1,53,1,54,1,54,1,54,1,55,1,55,1, - 55,1,55,3,55,549,8,55,1,55,1,55,1,55,1,55,5,55,555,8,55,10,55,12,55,558, - 9,55,3,55,560,8,55,1,56,1,56,1,56,3,56,565,8,56,1,56,1,56,1,57,1,57,1,57, - 1,57,1,57,1,58,1,58,1,58,1,58,3,58,578,8,58,1,58,0,4,2,10,18,20,59,0,2, - 4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52, - 54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100, - 102,104,106,108,110,112,114,116,0,8,1,0,59,60,1,0,61,63,2,0,26,26,76,76, - 1,0,67,68,2,0,31,31,35,35,2,0,38,38,41,41,2,0,37,37,51,51,2,0,52,52,54, - 58,606,0,118,1,0,0,0,2,121,1,0,0,0,4,138,1,0,0,0,6,156,1,0,0,0,8,158,1, - 0,0,0,10,191,1,0,0,0,12,218,1,0,0,0,14,220,1,0,0,0,16,229,1,0,0,0,18,235, - 1,0,0,0,20,256,1,0,0,0,22,266,1,0,0,0,24,281,1,0,0,0,26,283,1,0,0,0,28, - 286,1,0,0,0,30,299,1,0,0,0,32,301,1,0,0,0,34,318,1,0,0,0,36,320,1,0,0,0, - 38,322,1,0,0,0,40,326,1,0,0,0,42,328,1,0,0,0,44,337,1,0,0,0,46,341,1,0, - 0,0,48,357,1,0,0,0,50,360,1,0,0,0,52,368,1,0,0,0,54,376,1,0,0,0,56,384, - 1,0,0,0,58,392,1,0,0,0,60,396,1,0,0,0,62,440,1,0,0,0,64,444,1,0,0,0,66, - 448,1,0,0,0,68,450,1,0,0,0,70,453,1,0,0,0,72,462,1,0,0,0,74,470,1,0,0,0, - 76,473,1,0,0,0,78,476,1,0,0,0,80,485,1,0,0,0,82,489,1,0,0,0,84,495,1,0, - 0,0,86,499,1,0,0,0,88,502,1,0,0,0,90,510,1,0,0,0,92,514,1,0,0,0,94,518, - 1,0,0,0,96,521,1,0,0,0,98,526,1,0,0,0,100,530,1,0,0,0,102,532,1,0,0,0,104, - 534,1,0,0,0,106,537,1,0,0,0,108,541,1,0,0,0,110,544,1,0,0,0,112,564,1,0, - 0,0,114,568,1,0,0,0,116,573,1,0,0,0,118,119,3,2,1,0,119,120,5,0,0,1,120, - 1,1,0,0,0,121,122,6,1,-1,0,122,123,3,4,2,0,123,129,1,0,0,0,124,125,10,1, - 0,0,125,126,5,25,0,0,126,128,3,6,3,0,127,124,1,0,0,0,128,131,1,0,0,0,129, - 127,1,0,0,0,129,130,1,0,0,0,130,3,1,0,0,0,131,129,1,0,0,0,132,139,3,104, - 52,0,133,139,3,32,16,0,134,139,3,26,13,0,135,139,3,108,54,0,136,137,4,2, - 1,0,137,139,3,46,23,0,138,132,1,0,0,0,138,133,1,0,0,0,138,134,1,0,0,0,138, - 135,1,0,0,0,138,136,1,0,0,0,139,5,1,0,0,0,140,157,3,48,24,0,141,157,3,8, - 4,0,142,157,3,74,37,0,143,157,3,68,34,0,144,157,3,50,25,0,145,157,3,70, - 35,0,146,157,3,76,38,0,147,157,3,78,39,0,148,157,3,82,41,0,149,157,3,84, - 42,0,150,157,3,110,55,0,151,157,3,86,43,0,152,153,4,3,2,0,153,157,3,116, - 58,0,154,155,4,3,3,0,155,157,3,114,57,0,156,140,1,0,0,0,156,141,1,0,0,0, - 156,142,1,0,0,0,156,143,1,0,0,0,156,144,1,0,0,0,156,145,1,0,0,0,156,146, - 1,0,0,0,156,147,1,0,0,0,156,148,1,0,0,0,156,149,1,0,0,0,156,150,1,0,0,0, - 156,151,1,0,0,0,156,152,1,0,0,0,156,154,1,0,0,0,157,7,1,0,0,0,158,159,5, - 16,0,0,159,160,3,10,5,0,160,9,1,0,0,0,161,162,6,5,-1,0,162,163,5,44,0,0, - 163,192,3,10,5,8,164,192,3,16,8,0,165,192,3,12,6,0,166,168,3,16,8,0,167, - 169,5,44,0,0,168,167,1,0,0,0,168,169,1,0,0,0,169,170,1,0,0,0,170,171,5, - 39,0,0,171,172,5,43,0,0,172,177,3,16,8,0,173,174,5,34,0,0,174,176,3,16, - 8,0,175,173,1,0,0,0,176,179,1,0,0,0,177,175,1,0,0,0,177,178,1,0,0,0,178, - 180,1,0,0,0,179,177,1,0,0,0,180,181,5,50,0,0,181,192,1,0,0,0,182,183,3, - 16,8,0,183,185,5,40,0,0,184,186,5,44,0,0,185,184,1,0,0,0,185,186,1,0,0, - 0,186,187,1,0,0,0,187,188,5,45,0,0,188,192,1,0,0,0,189,190,4,5,4,0,190, - 192,3,14,7,0,191,161,1,0,0,0,191,164,1,0,0,0,191,165,1,0,0,0,191,166,1, - 0,0,0,191,182,1,0,0,0,191,189,1,0,0,0,192,201,1,0,0,0,193,194,10,5,0,0, - 194,195,5,30,0,0,195,200,3,10,5,6,196,197,10,4,0,0,197,198,5,47,0,0,198, - 200,3,10,5,5,199,193,1,0,0,0,199,196,1,0,0,0,200,203,1,0,0,0,201,199,1, - 0,0,0,201,202,1,0,0,0,202,11,1,0,0,0,203,201,1,0,0,0,204,206,3,16,8,0,205, - 207,5,44,0,0,206,205,1,0,0,0,206,207,1,0,0,0,207,208,1,0,0,0,208,209,5, - 42,0,0,209,210,3,100,50,0,210,219,1,0,0,0,211,213,3,16,8,0,212,214,5,44, - 0,0,213,212,1,0,0,0,213,214,1,0,0,0,214,215,1,0,0,0,215,216,5,49,0,0,216, - 217,3,100,50,0,217,219,1,0,0,0,218,204,1,0,0,0,218,211,1,0,0,0,219,13,1, - 0,0,0,220,221,3,16,8,0,221,222,5,19,0,0,222,223,3,100,50,0,223,15,1,0,0, - 0,224,230,3,18,9,0,225,226,3,18,9,0,226,227,3,102,51,0,227,228,3,18,9,0, - 228,230,1,0,0,0,229,224,1,0,0,0,229,225,1,0,0,0,230,17,1,0,0,0,231,232, - 6,9,-1,0,232,236,3,20,10,0,233,234,7,0,0,0,234,236,3,18,9,3,235,231,1,0, - 0,0,235,233,1,0,0,0,236,245,1,0,0,0,237,238,10,2,0,0,238,239,7,1,0,0,239, - 244,3,18,9,3,240,241,10,1,0,0,241,242,7,0,0,0,242,244,3,18,9,2,243,237, - 1,0,0,0,243,240,1,0,0,0,244,247,1,0,0,0,245,243,1,0,0,0,245,246,1,0,0,0, - 246,19,1,0,0,0,247,245,1,0,0,0,248,249,6,10,-1,0,249,257,3,62,31,0,250, - 257,3,52,26,0,251,257,3,22,11,0,252,253,5,43,0,0,253,254,3,10,5,0,254,255, - 5,50,0,0,255,257,1,0,0,0,256,248,1,0,0,0,256,250,1,0,0,0,256,251,1,0,0, - 0,256,252,1,0,0,0,257,263,1,0,0,0,258,259,10,1,0,0,259,260,5,33,0,0,260, - 262,3,24,12,0,261,258,1,0,0,0,262,265,1,0,0,0,263,261,1,0,0,0,263,264,1, - 0,0,0,264,21,1,0,0,0,265,263,1,0,0,0,266,267,3,66,33,0,267,277,5,43,0,0, - 268,278,5,61,0,0,269,274,3,10,5,0,270,271,5,34,0,0,271,273,3,10,5,0,272, - 270,1,0,0,0,273,276,1,0,0,0,274,272,1,0,0,0,274,275,1,0,0,0,275,278,1,0, - 0,0,276,274,1,0,0,0,277,268,1,0,0,0,277,269,1,0,0,0,277,278,1,0,0,0,278, - 279,1,0,0,0,279,280,5,50,0,0,280,23,1,0,0,0,281,282,3,58,29,0,282,25,1, - 0,0,0,283,284,5,12,0,0,284,285,3,28,14,0,285,27,1,0,0,0,286,291,3,30,15, - 0,287,288,5,34,0,0,288,290,3,30,15,0,289,287,1,0,0,0,290,293,1,0,0,0,291, - 289,1,0,0,0,291,292,1,0,0,0,292,29,1,0,0,0,293,291,1,0,0,0,294,300,3,10, - 5,0,295,296,3,52,26,0,296,297,5,32,0,0,297,298,3,10,5,0,298,300,1,0,0,0, - 299,294,1,0,0,0,299,295,1,0,0,0,300,31,1,0,0,0,301,302,5,6,0,0,302,307, - 3,34,17,0,303,304,5,34,0,0,304,306,3,34,17,0,305,303,1,0,0,0,306,309,1, - 0,0,0,307,305,1,0,0,0,307,308,1,0,0,0,308,311,1,0,0,0,309,307,1,0,0,0,310, - 312,3,40,20,0,311,310,1,0,0,0,311,312,1,0,0,0,312,33,1,0,0,0,313,314,3, - 36,18,0,314,315,5,104,0,0,315,316,3,38,19,0,316,319,1,0,0,0,317,319,3,38, - 19,0,318,313,1,0,0,0,318,317,1,0,0,0,319,35,1,0,0,0,320,321,5,76,0,0,321, - 37,1,0,0,0,322,323,7,2,0,0,323,39,1,0,0,0,324,327,3,42,21,0,325,327,3,44, - 22,0,326,324,1,0,0,0,326,325,1,0,0,0,327,41,1,0,0,0,328,329,5,75,0,0,329, - 334,5,76,0,0,330,331,5,34,0,0,331,333,5,76,0,0,332,330,1,0,0,0,333,336, - 1,0,0,0,334,332,1,0,0,0,334,335,1,0,0,0,335,43,1,0,0,0,336,334,1,0,0,0, - 337,338,5,65,0,0,338,339,3,42,21,0,339,340,5,66,0,0,340,45,1,0,0,0,341, - 342,5,20,0,0,342,347,3,34,17,0,343,344,5,34,0,0,344,346,3,34,17,0,345,343, - 1,0,0,0,346,349,1,0,0,0,347,345,1,0,0,0,347,348,1,0,0,0,348,351,1,0,0,0, - 349,347,1,0,0,0,350,352,3,28,14,0,351,350,1,0,0,0,351,352,1,0,0,0,352,355, - 1,0,0,0,353,354,5,29,0,0,354,356,3,28,14,0,355,353,1,0,0,0,355,356,1,0, - 0,0,356,47,1,0,0,0,357,358,5,4,0,0,358,359,3,28,14,0,359,49,1,0,0,0,360, - 362,5,15,0,0,361,363,3,28,14,0,362,361,1,0,0,0,362,363,1,0,0,0,363,366, - 1,0,0,0,364,365,5,29,0,0,365,367,3,28,14,0,366,364,1,0,0,0,366,367,1,0, - 0,0,367,51,1,0,0,0,368,373,3,66,33,0,369,370,5,36,0,0,370,372,3,66,33,0, - 371,369,1,0,0,0,372,375,1,0,0,0,373,371,1,0,0,0,373,374,1,0,0,0,374,53, - 1,0,0,0,375,373,1,0,0,0,376,381,3,60,30,0,377,378,5,36,0,0,378,380,3,60, - 30,0,379,377,1,0,0,0,380,383,1,0,0,0,381,379,1,0,0,0,381,382,1,0,0,0,382, - 55,1,0,0,0,383,381,1,0,0,0,384,389,3,54,27,0,385,386,5,34,0,0,386,388,3, - 54,27,0,387,385,1,0,0,0,388,391,1,0,0,0,389,387,1,0,0,0,389,390,1,0,0,0, - 390,57,1,0,0,0,391,389,1,0,0,0,392,393,7,3,0,0,393,59,1,0,0,0,394,397,5, - 80,0,0,395,397,3,64,32,0,396,394,1,0,0,0,396,395,1,0,0,0,397,61,1,0,0,0, - 398,441,5,45,0,0,399,400,3,98,49,0,400,401,5,67,0,0,401,441,1,0,0,0,402, - 441,3,96,48,0,403,441,3,98,49,0,404,441,3,92,46,0,405,441,3,64,32,0,406, - 441,3,100,50,0,407,408,5,65,0,0,408,413,3,94,47,0,409,410,5,34,0,0,410, - 412,3,94,47,0,411,409,1,0,0,0,412,415,1,0,0,0,413,411,1,0,0,0,413,414,1, - 0,0,0,414,416,1,0,0,0,415,413,1,0,0,0,416,417,5,66,0,0,417,441,1,0,0,0, - 418,419,5,65,0,0,419,424,3,92,46,0,420,421,5,34,0,0,421,423,3,92,46,0,422, - 420,1,0,0,0,423,426,1,0,0,0,424,422,1,0,0,0,424,425,1,0,0,0,425,427,1,0, - 0,0,426,424,1,0,0,0,427,428,5,66,0,0,428,441,1,0,0,0,429,430,5,65,0,0,430, - 435,3,100,50,0,431,432,5,34,0,0,432,434,3,100,50,0,433,431,1,0,0,0,434, - 437,1,0,0,0,435,433,1,0,0,0,435,436,1,0,0,0,436,438,1,0,0,0,437,435,1,0, - 0,0,438,439,5,66,0,0,439,441,1,0,0,0,440,398,1,0,0,0,440,399,1,0,0,0,440, - 402,1,0,0,0,440,403,1,0,0,0,440,404,1,0,0,0,440,405,1,0,0,0,440,406,1,0, - 0,0,440,407,1,0,0,0,440,418,1,0,0,0,440,429,1,0,0,0,441,63,1,0,0,0,442, - 445,5,48,0,0,443,445,5,64,0,0,444,442,1,0,0,0,444,443,1,0,0,0,445,65,1, - 0,0,0,446,449,3,58,29,0,447,449,3,64,32,0,448,446,1,0,0,0,448,447,1,0,0, - 0,449,67,1,0,0,0,450,451,5,9,0,0,451,452,5,27,0,0,452,69,1,0,0,0,453,454, - 5,14,0,0,454,459,3,72,36,0,455,456,5,34,0,0,456,458,3,72,36,0,457,455,1, - 0,0,0,458,461,1,0,0,0,459,457,1,0,0,0,459,460,1,0,0,0,460,71,1,0,0,0,461, - 459,1,0,0,0,462,464,3,10,5,0,463,465,7,4,0,0,464,463,1,0,0,0,464,465,1, - 0,0,0,465,468,1,0,0,0,466,467,5,46,0,0,467,469,7,5,0,0,468,466,1,0,0,0, - 468,469,1,0,0,0,469,73,1,0,0,0,470,471,5,8,0,0,471,472,3,56,28,0,472,75, - 1,0,0,0,473,474,5,2,0,0,474,475,3,56,28,0,475,77,1,0,0,0,476,477,5,11,0, - 0,477,482,3,80,40,0,478,479,5,34,0,0,479,481,3,80,40,0,480,478,1,0,0,0, - 481,484,1,0,0,0,482,480,1,0,0,0,482,483,1,0,0,0,483,79,1,0,0,0,484,482, - 1,0,0,0,485,486,3,54,27,0,486,487,5,84,0,0,487,488,3,54,27,0,488,81,1,0, - 0,0,489,490,5,1,0,0,490,491,3,20,10,0,491,493,3,100,50,0,492,494,3,88,44, - 0,493,492,1,0,0,0,493,494,1,0,0,0,494,83,1,0,0,0,495,496,5,7,0,0,496,497, - 3,20,10,0,497,498,3,100,50,0,498,85,1,0,0,0,499,500,5,10,0,0,500,501,3, - 52,26,0,501,87,1,0,0,0,502,507,3,90,45,0,503,504,5,34,0,0,504,506,3,90, - 45,0,505,503,1,0,0,0,506,509,1,0,0,0,507,505,1,0,0,0,507,508,1,0,0,0,508, - 89,1,0,0,0,509,507,1,0,0,0,510,511,3,58,29,0,511,512,5,32,0,0,512,513,3, - 62,31,0,513,91,1,0,0,0,514,515,7,6,0,0,515,93,1,0,0,0,516,519,3,96,48,0, - 517,519,3,98,49,0,518,516,1,0,0,0,518,517,1,0,0,0,519,95,1,0,0,0,520,522, - 7,0,0,0,521,520,1,0,0,0,521,522,1,0,0,0,522,523,1,0,0,0,523,524,5,28,0, - 0,524,97,1,0,0,0,525,527,7,0,0,0,526,525,1,0,0,0,526,527,1,0,0,0,527,528, - 1,0,0,0,528,529,5,27,0,0,529,99,1,0,0,0,530,531,5,26,0,0,531,101,1,0,0, - 0,532,533,7,7,0,0,533,103,1,0,0,0,534,535,5,5,0,0,535,536,3,106,53,0,536, - 105,1,0,0,0,537,538,5,65,0,0,538,539,3,2,1,0,539,540,5,66,0,0,540,107,1, - 0,0,0,541,542,5,13,0,0,542,543,5,100,0,0,543,109,1,0,0,0,544,545,5,3,0, - 0,545,548,5,90,0,0,546,547,5,88,0,0,547,549,3,54,27,0,548,546,1,0,0,0,548, - 549,1,0,0,0,549,559,1,0,0,0,550,551,5,89,0,0,551,556,3,112,56,0,552,553, - 5,34,0,0,553,555,3,112,56,0,554,552,1,0,0,0,555,558,1,0,0,0,556,554,1,0, - 0,0,556,557,1,0,0,0,557,560,1,0,0,0,558,556,1,0,0,0,559,550,1,0,0,0,559, - 560,1,0,0,0,560,111,1,0,0,0,561,562,3,54,27,0,562,563,5,32,0,0,563,565, - 1,0,0,0,564,561,1,0,0,0,564,565,1,0,0,0,565,566,1,0,0,0,566,567,3,54,27, - 0,567,113,1,0,0,0,568,569,5,18,0,0,569,570,3,34,17,0,570,571,5,88,0,0,571, - 572,3,56,28,0,572,115,1,0,0,0,573,574,5,17,0,0,574,577,3,28,14,0,575,576, - 5,29,0,0,576,578,3,28,14,0,577,575,1,0,0,0,577,578,1,0,0,0,578,117,1,0, - 0,0,56,129,138,156,168,177,185,191,199,201,206,213,218,229,235,243,245, - 256,263,274,277,291,299,307,311,318,326,334,347,351,355,362,366,373,381, - 389,396,413,424,435,440,444,448,459,464,468,482,493,507,518,521,526,548, - 556,559,564,577]; + 7,53,2,54,7,54,2,55,7,55,2,56,7,56,2,57,7,57,2,58,7,58,2,59,7,59,2,60,7, + 60,2,61,7,61,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,5,1,134,8,1,10,1,12,1, + 137,9,1,1,2,1,2,1,2,1,2,1,2,1,2,3,2,145,8,2,1,3,1,3,1,3,1,3,1,3,1,3,1,3, + 1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,3,3,163,8,3,1,4,1,4,1,4,1,5,1,5,1,5, + 1,5,1,5,1,5,1,5,3,5,175,8,5,1,5,1,5,1,5,1,5,1,5,5,5,182,8,5,10,5,12,5,185, + 9,5,1,5,1,5,1,5,1,5,1,5,3,5,192,8,5,1,5,1,5,1,5,1,5,3,5,198,8,5,1,5,1,5, + 1,5,1,5,1,5,1,5,5,5,206,8,5,10,5,12,5,209,9,5,1,6,1,6,3,6,213,8,6,1,6,1, + 6,1,6,1,6,1,6,3,6,220,8,6,1,6,1,6,1,6,3,6,225,8,6,1,7,1,7,1,7,1,7,1,8,1, + 8,1,8,1,8,1,8,3,8,236,8,8,1,9,1,9,1,9,1,9,3,9,242,8,9,1,9,1,9,1,9,1,9,1, + 9,1,9,5,9,250,8,9,10,9,12,9,253,9,9,1,10,1,10,1,10,1,10,1,10,1,10,1,10, + 1,10,3,10,263,8,10,1,10,1,10,1,10,5,10,268,8,10,10,10,12,10,271,9,10,1, + 11,1,11,1,11,1,11,1,11,1,11,5,11,279,8,11,10,11,12,11,282,9,11,3,11,284, + 8,11,1,11,1,11,1,12,1,12,3,12,290,8,12,1,13,1,13,1,14,1,14,1,14,1,15,1, + 15,1,15,5,15,300,8,15,10,15,12,15,303,9,15,1,16,1,16,1,16,3,16,308,8,16, + 1,16,1,16,1,17,1,17,1,17,1,17,5,17,316,8,17,10,17,12,17,319,9,17,1,17,3, + 17,322,8,17,1,18,1,18,1,18,3,18,327,8,18,1,18,1,18,1,19,1,19,1,20,1,20, + 1,21,1,21,3,21,337,8,21,1,22,1,22,1,22,1,22,5,22,343,8,22,10,22,12,22,346, + 9,22,1,23,1,23,1,23,1,23,1,24,1,24,1,24,1,24,5,24,356,8,24,10,24,12,24, + 359,9,24,1,24,3,24,362,8,24,1,24,1,24,3,24,366,8,24,1,25,1,25,1,25,1,26, + 1,26,3,26,373,8,26,1,26,1,26,3,26,377,8,26,1,27,1,27,1,27,5,27,382,8,27, + 10,27,12,27,385,9,27,1,28,1,28,1,28,3,28,390,8,28,1,29,1,29,1,29,5,29,395, + 8,29,10,29,12,29,398,9,29,1,30,1,30,1,30,5,30,403,8,30,10,30,12,30,406, + 9,30,1,31,1,31,1,31,5,31,411,8,31,10,31,12,31,414,9,31,1,32,1,32,1,33,1, + 33,1,33,3,33,421,8,33,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34, + 1,34,1,34,1,34,5,34,436,8,34,10,34,12,34,439,9,34,1,34,1,34,1,34,1,34,1, + 34,1,34,5,34,447,8,34,10,34,12,34,450,9,34,1,34,1,34,1,34,1,34,1,34,1,34, + 5,34,458,8,34,10,34,12,34,461,9,34,1,34,1,34,3,34,465,8,34,1,35,1,35,3, + 35,469,8,35,1,36,1,36,1,36,3,36,474,8,36,1,37,1,37,1,37,1,38,1,38,1,38, + 1,38,5,38,483,8,38,10,38,12,38,486,9,38,1,39,1,39,3,39,490,8,39,1,39,1, + 39,3,39,494,8,39,1,40,1,40,1,40,1,41,1,41,1,41,1,42,1,42,1,42,1,42,5,42, + 506,8,42,10,42,12,42,509,9,42,1,43,1,43,1,43,1,43,1,44,1,44,1,44,1,44,3, + 44,519,8,44,1,45,1,45,1,45,1,45,1,46,1,46,1,46,1,47,1,47,1,47,5,47,531, + 8,47,10,47,12,47,534,9,47,1,48,1,48,1,48,1,48,1,49,1,49,1,50,1,50,3,50, + 544,8,50,1,51,3,51,547,8,51,1,51,1,51,1,52,3,52,552,8,52,1,52,1,52,1,53, + 1,53,1,54,1,54,1,55,1,55,1,55,1,56,1,56,1,56,1,56,1,57,1,57,1,57,1,58,1, + 58,1,58,1,58,3,58,574,8,58,1,58,1,58,1,58,1,58,5,58,580,8,58,10,58,12,58, + 583,9,58,3,58,585,8,58,1,59,1,59,1,59,3,59,590,8,59,1,59,1,59,1,60,1,60, + 1,60,1,60,1,60,1,61,1,61,1,61,1,61,3,61,603,8,61,1,61,0,4,2,10,18,20,62, + 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50, + 52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98, + 100,102,104,106,108,110,112,114,116,118,120,122,0,8,1,0,58,59,1,0,60,62, + 2,0,25,25,76,76,1,0,67,68,2,0,30,30,34,34,2,0,37,37,40,40,2,0,36,36,50, + 50,2,0,51,51,53,57,631,0,124,1,0,0,0,2,127,1,0,0,0,4,144,1,0,0,0,6,162, + 1,0,0,0,8,164,1,0,0,0,10,197,1,0,0,0,12,224,1,0,0,0,14,226,1,0,0,0,16,235, + 1,0,0,0,18,241,1,0,0,0,20,262,1,0,0,0,22,272,1,0,0,0,24,289,1,0,0,0,26, + 291,1,0,0,0,28,293,1,0,0,0,30,296,1,0,0,0,32,307,1,0,0,0,34,311,1,0,0,0, + 36,326,1,0,0,0,38,330,1,0,0,0,40,332,1,0,0,0,42,336,1,0,0,0,44,338,1,0, + 0,0,46,347,1,0,0,0,48,351,1,0,0,0,50,367,1,0,0,0,52,370,1,0,0,0,54,378, + 1,0,0,0,56,386,1,0,0,0,58,391,1,0,0,0,60,399,1,0,0,0,62,407,1,0,0,0,64, + 415,1,0,0,0,66,420,1,0,0,0,68,464,1,0,0,0,70,468,1,0,0,0,72,473,1,0,0,0, + 74,475,1,0,0,0,76,478,1,0,0,0,78,487,1,0,0,0,80,495,1,0,0,0,82,498,1,0, + 0,0,84,501,1,0,0,0,86,510,1,0,0,0,88,514,1,0,0,0,90,520,1,0,0,0,92,524, + 1,0,0,0,94,527,1,0,0,0,96,535,1,0,0,0,98,539,1,0,0,0,100,543,1,0,0,0,102, + 546,1,0,0,0,104,551,1,0,0,0,106,555,1,0,0,0,108,557,1,0,0,0,110,559,1,0, + 0,0,112,562,1,0,0,0,114,566,1,0,0,0,116,569,1,0,0,0,118,589,1,0,0,0,120, + 593,1,0,0,0,122,598,1,0,0,0,124,125,3,2,1,0,125,126,5,0,0,1,126,1,1,0,0, + 0,127,128,6,1,-1,0,128,129,3,4,2,0,129,135,1,0,0,0,130,131,10,1,0,0,131, + 132,5,24,0,0,132,134,3,6,3,0,133,130,1,0,0,0,134,137,1,0,0,0,135,133,1, + 0,0,0,135,136,1,0,0,0,136,3,1,0,0,0,137,135,1,0,0,0,138,145,3,110,55,0, + 139,145,3,34,17,0,140,145,3,28,14,0,141,145,3,114,57,0,142,143,4,2,1,0, + 143,145,3,48,24,0,144,138,1,0,0,0,144,139,1,0,0,0,144,140,1,0,0,0,144,141, + 1,0,0,0,144,142,1,0,0,0,145,5,1,0,0,0,146,163,3,50,25,0,147,163,3,8,4,0, + 148,163,3,80,40,0,149,163,3,74,37,0,150,163,3,52,26,0,151,163,3,76,38,0, + 152,163,3,82,41,0,153,163,3,84,42,0,154,163,3,88,44,0,155,163,3,90,45,0, + 156,163,3,116,58,0,157,163,3,92,46,0,158,159,4,3,2,0,159,163,3,122,61,0, + 160,161,4,3,3,0,161,163,3,120,60,0,162,146,1,0,0,0,162,147,1,0,0,0,162, + 148,1,0,0,0,162,149,1,0,0,0,162,150,1,0,0,0,162,151,1,0,0,0,162,152,1,0, + 0,0,162,153,1,0,0,0,162,154,1,0,0,0,162,155,1,0,0,0,162,156,1,0,0,0,162, + 157,1,0,0,0,162,158,1,0,0,0,162,160,1,0,0,0,163,7,1,0,0,0,164,165,5,16, + 0,0,165,166,3,10,5,0,166,9,1,0,0,0,167,168,6,5,-1,0,168,169,5,43,0,0,169, + 198,3,10,5,8,170,198,3,16,8,0,171,198,3,12,6,0,172,174,3,16,8,0,173,175, + 5,43,0,0,174,173,1,0,0,0,174,175,1,0,0,0,175,176,1,0,0,0,176,177,5,38,0, + 0,177,178,5,42,0,0,178,183,3,16,8,0,179,180,5,33,0,0,180,182,3,16,8,0,181, + 179,1,0,0,0,182,185,1,0,0,0,183,181,1,0,0,0,183,184,1,0,0,0,184,186,1,0, + 0,0,185,183,1,0,0,0,186,187,5,49,0,0,187,198,1,0,0,0,188,189,3,16,8,0,189, + 191,5,39,0,0,190,192,5,43,0,0,191,190,1,0,0,0,191,192,1,0,0,0,192,193,1, + 0,0,0,193,194,5,44,0,0,194,198,1,0,0,0,195,196,4,5,4,0,196,198,3,14,7,0, + 197,167,1,0,0,0,197,170,1,0,0,0,197,171,1,0,0,0,197,172,1,0,0,0,197,188, + 1,0,0,0,197,195,1,0,0,0,198,207,1,0,0,0,199,200,10,5,0,0,200,201,5,29,0, + 0,201,206,3,10,5,6,202,203,10,4,0,0,203,204,5,46,0,0,204,206,3,10,5,5,205, + 199,1,0,0,0,205,202,1,0,0,0,206,209,1,0,0,0,207,205,1,0,0,0,207,208,1,0, + 0,0,208,11,1,0,0,0,209,207,1,0,0,0,210,212,3,16,8,0,211,213,5,43,0,0,212, + 211,1,0,0,0,212,213,1,0,0,0,213,214,1,0,0,0,214,215,5,41,0,0,215,216,3, + 106,53,0,216,225,1,0,0,0,217,219,3,16,8,0,218,220,5,43,0,0,219,218,1,0, + 0,0,219,220,1,0,0,0,220,221,1,0,0,0,221,222,5,48,0,0,222,223,3,106,53,0, + 223,225,1,0,0,0,224,210,1,0,0,0,224,217,1,0,0,0,225,13,1,0,0,0,226,227, + 3,16,8,0,227,228,5,63,0,0,228,229,3,106,53,0,229,15,1,0,0,0,230,236,3,18, + 9,0,231,232,3,18,9,0,232,233,3,108,54,0,233,234,3,18,9,0,234,236,1,0,0, + 0,235,230,1,0,0,0,235,231,1,0,0,0,236,17,1,0,0,0,237,238,6,9,-1,0,238,242, + 3,20,10,0,239,240,7,0,0,0,240,242,3,18,9,3,241,237,1,0,0,0,241,239,1,0, + 0,0,242,251,1,0,0,0,243,244,10,2,0,0,244,245,7,1,0,0,245,250,3,18,9,3,246, + 247,10,1,0,0,247,248,7,0,0,0,248,250,3,18,9,2,249,243,1,0,0,0,249,246,1, + 0,0,0,250,253,1,0,0,0,251,249,1,0,0,0,251,252,1,0,0,0,252,19,1,0,0,0,253, + 251,1,0,0,0,254,255,6,10,-1,0,255,263,3,68,34,0,256,263,3,58,29,0,257,263, + 3,22,11,0,258,259,5,42,0,0,259,260,3,10,5,0,260,261,5,49,0,0,261,263,1, + 0,0,0,262,254,1,0,0,0,262,256,1,0,0,0,262,257,1,0,0,0,262,258,1,0,0,0,263, + 269,1,0,0,0,264,265,10,1,0,0,265,266,5,32,0,0,266,268,3,26,13,0,267,264, + 1,0,0,0,268,271,1,0,0,0,269,267,1,0,0,0,269,270,1,0,0,0,270,21,1,0,0,0, + 271,269,1,0,0,0,272,273,3,24,12,0,273,283,5,42,0,0,274,284,5,60,0,0,275, + 280,3,10,5,0,276,277,5,33,0,0,277,279,3,10,5,0,278,276,1,0,0,0,279,282, + 1,0,0,0,280,278,1,0,0,0,280,281,1,0,0,0,281,284,1,0,0,0,282,280,1,0,0,0, + 283,274,1,0,0,0,283,275,1,0,0,0,283,284,1,0,0,0,284,285,1,0,0,0,285,286, + 5,49,0,0,286,23,1,0,0,0,287,290,5,63,0,0,288,290,3,72,36,0,289,287,1,0, + 0,0,289,288,1,0,0,0,290,25,1,0,0,0,291,292,3,64,32,0,292,27,1,0,0,0,293, + 294,5,12,0,0,294,295,3,30,15,0,295,29,1,0,0,0,296,301,3,32,16,0,297,298, + 5,33,0,0,298,300,3,32,16,0,299,297,1,0,0,0,300,303,1,0,0,0,301,299,1,0, + 0,0,301,302,1,0,0,0,302,31,1,0,0,0,303,301,1,0,0,0,304,305,3,58,29,0,305, + 306,5,31,0,0,306,308,1,0,0,0,307,304,1,0,0,0,307,308,1,0,0,0,308,309,1, + 0,0,0,309,310,3,10,5,0,310,33,1,0,0,0,311,312,5,6,0,0,312,317,3,36,18,0, + 313,314,5,33,0,0,314,316,3,36,18,0,315,313,1,0,0,0,316,319,1,0,0,0,317, + 315,1,0,0,0,317,318,1,0,0,0,318,321,1,0,0,0,319,317,1,0,0,0,320,322,3,42, + 21,0,321,320,1,0,0,0,321,322,1,0,0,0,322,35,1,0,0,0,323,324,3,38,19,0,324, + 325,5,104,0,0,325,327,1,0,0,0,326,323,1,0,0,0,326,327,1,0,0,0,327,328,1, + 0,0,0,328,329,3,40,20,0,329,37,1,0,0,0,330,331,5,76,0,0,331,39,1,0,0,0, + 332,333,7,2,0,0,333,41,1,0,0,0,334,337,3,44,22,0,335,337,3,46,23,0,336, + 334,1,0,0,0,336,335,1,0,0,0,337,43,1,0,0,0,338,339,5,75,0,0,339,344,5,76, + 0,0,340,341,5,33,0,0,341,343,5,76,0,0,342,340,1,0,0,0,343,346,1,0,0,0,344, + 342,1,0,0,0,344,345,1,0,0,0,345,45,1,0,0,0,346,344,1,0,0,0,347,348,5,65, + 0,0,348,349,3,44,22,0,349,350,5,66,0,0,350,47,1,0,0,0,351,352,5,19,0,0, + 352,357,3,36,18,0,353,354,5,33,0,0,354,356,3,36,18,0,355,353,1,0,0,0,356, + 359,1,0,0,0,357,355,1,0,0,0,357,358,1,0,0,0,358,361,1,0,0,0,359,357,1,0, + 0,0,360,362,3,54,27,0,361,360,1,0,0,0,361,362,1,0,0,0,362,365,1,0,0,0,363, + 364,5,28,0,0,364,366,3,30,15,0,365,363,1,0,0,0,365,366,1,0,0,0,366,49,1, + 0,0,0,367,368,5,4,0,0,368,369,3,30,15,0,369,51,1,0,0,0,370,372,5,15,0,0, + 371,373,3,54,27,0,372,371,1,0,0,0,372,373,1,0,0,0,373,376,1,0,0,0,374,375, + 5,28,0,0,375,377,3,30,15,0,376,374,1,0,0,0,376,377,1,0,0,0,377,53,1,0,0, + 0,378,383,3,56,28,0,379,380,5,33,0,0,380,382,3,56,28,0,381,379,1,0,0,0, + 382,385,1,0,0,0,383,381,1,0,0,0,383,384,1,0,0,0,384,55,1,0,0,0,385,383, + 1,0,0,0,386,389,3,32,16,0,387,388,5,16,0,0,388,390,3,10,5,0,389,387,1,0, + 0,0,389,390,1,0,0,0,390,57,1,0,0,0,391,396,3,72,36,0,392,393,5,35,0,0,393, + 395,3,72,36,0,394,392,1,0,0,0,395,398,1,0,0,0,396,394,1,0,0,0,396,397,1, + 0,0,0,397,59,1,0,0,0,398,396,1,0,0,0,399,404,3,66,33,0,400,401,5,35,0,0, + 401,403,3,66,33,0,402,400,1,0,0,0,403,406,1,0,0,0,404,402,1,0,0,0,404,405, + 1,0,0,0,405,61,1,0,0,0,406,404,1,0,0,0,407,412,3,60,30,0,408,409,5,33,0, + 0,409,411,3,60,30,0,410,408,1,0,0,0,411,414,1,0,0,0,412,410,1,0,0,0,412, + 413,1,0,0,0,413,63,1,0,0,0,414,412,1,0,0,0,415,416,7,3,0,0,416,65,1,0,0, + 0,417,421,5,80,0,0,418,419,4,33,10,0,419,421,3,70,35,0,420,417,1,0,0,0, + 420,418,1,0,0,0,421,67,1,0,0,0,422,465,5,44,0,0,423,424,3,104,52,0,424, + 425,5,67,0,0,425,465,1,0,0,0,426,465,3,102,51,0,427,465,3,104,52,0,428, + 465,3,98,49,0,429,465,3,70,35,0,430,465,3,106,53,0,431,432,5,65,0,0,432, + 437,3,100,50,0,433,434,5,33,0,0,434,436,3,100,50,0,435,433,1,0,0,0,436, + 439,1,0,0,0,437,435,1,0,0,0,437,438,1,0,0,0,438,440,1,0,0,0,439,437,1,0, + 0,0,440,441,5,66,0,0,441,465,1,0,0,0,442,443,5,65,0,0,443,448,3,98,49,0, + 444,445,5,33,0,0,445,447,3,98,49,0,446,444,1,0,0,0,447,450,1,0,0,0,448, + 446,1,0,0,0,448,449,1,0,0,0,449,451,1,0,0,0,450,448,1,0,0,0,451,452,5,66, + 0,0,452,465,1,0,0,0,453,454,5,65,0,0,454,459,3,106,53,0,455,456,5,33,0, + 0,456,458,3,106,53,0,457,455,1,0,0,0,458,461,1,0,0,0,459,457,1,0,0,0,459, + 460,1,0,0,0,460,462,1,0,0,0,461,459,1,0,0,0,462,463,5,66,0,0,463,465,1, + 0,0,0,464,422,1,0,0,0,464,423,1,0,0,0,464,426,1,0,0,0,464,427,1,0,0,0,464, + 428,1,0,0,0,464,429,1,0,0,0,464,430,1,0,0,0,464,431,1,0,0,0,464,442,1,0, + 0,0,464,453,1,0,0,0,465,69,1,0,0,0,466,469,5,47,0,0,467,469,5,64,0,0,468, + 466,1,0,0,0,468,467,1,0,0,0,469,71,1,0,0,0,470,474,3,64,32,0,471,472,4, + 36,11,0,472,474,3,70,35,0,473,470,1,0,0,0,473,471,1,0,0,0,474,73,1,0,0, + 0,475,476,5,9,0,0,476,477,5,26,0,0,477,75,1,0,0,0,478,479,5,14,0,0,479, + 484,3,78,39,0,480,481,5,33,0,0,481,483,3,78,39,0,482,480,1,0,0,0,483,486, + 1,0,0,0,484,482,1,0,0,0,484,485,1,0,0,0,485,77,1,0,0,0,486,484,1,0,0,0, + 487,489,3,10,5,0,488,490,7,4,0,0,489,488,1,0,0,0,489,490,1,0,0,0,490,493, + 1,0,0,0,491,492,5,45,0,0,492,494,7,5,0,0,493,491,1,0,0,0,493,494,1,0,0, + 0,494,79,1,0,0,0,495,496,5,8,0,0,496,497,3,62,31,0,497,81,1,0,0,0,498,499, + 5,2,0,0,499,500,3,62,31,0,500,83,1,0,0,0,501,502,5,11,0,0,502,507,3,86, + 43,0,503,504,5,33,0,0,504,506,3,86,43,0,505,503,1,0,0,0,506,509,1,0,0,0, + 507,505,1,0,0,0,507,508,1,0,0,0,508,85,1,0,0,0,509,507,1,0,0,0,510,511, + 3,60,30,0,511,512,5,84,0,0,512,513,3,60,30,0,513,87,1,0,0,0,514,515,5,1, + 0,0,515,516,3,20,10,0,516,518,3,106,53,0,517,519,3,94,47,0,518,517,1,0, + 0,0,518,519,1,0,0,0,519,89,1,0,0,0,520,521,5,7,0,0,521,522,3,20,10,0,522, + 523,3,106,53,0,523,91,1,0,0,0,524,525,5,10,0,0,525,526,3,58,29,0,526,93, + 1,0,0,0,527,532,3,96,48,0,528,529,5,33,0,0,529,531,3,96,48,0,530,528,1, + 0,0,0,531,534,1,0,0,0,532,530,1,0,0,0,532,533,1,0,0,0,533,95,1,0,0,0,534, + 532,1,0,0,0,535,536,3,64,32,0,536,537,5,31,0,0,537,538,3,68,34,0,538,97, + 1,0,0,0,539,540,7,6,0,0,540,99,1,0,0,0,541,544,3,102,51,0,542,544,3,104, + 52,0,543,541,1,0,0,0,543,542,1,0,0,0,544,101,1,0,0,0,545,547,7,0,0,0,546, + 545,1,0,0,0,546,547,1,0,0,0,547,548,1,0,0,0,548,549,5,27,0,0,549,103,1, + 0,0,0,550,552,7,0,0,0,551,550,1,0,0,0,551,552,1,0,0,0,552,553,1,0,0,0,553, + 554,5,26,0,0,554,105,1,0,0,0,555,556,5,25,0,0,556,107,1,0,0,0,557,558,7, + 7,0,0,558,109,1,0,0,0,559,560,5,5,0,0,560,561,3,112,56,0,561,111,1,0,0, + 0,562,563,5,65,0,0,563,564,3,2,1,0,564,565,5,66,0,0,565,113,1,0,0,0,566, + 567,5,13,0,0,567,568,5,100,0,0,568,115,1,0,0,0,569,570,5,3,0,0,570,573, + 5,90,0,0,571,572,5,88,0,0,572,574,3,60,30,0,573,571,1,0,0,0,573,574,1,0, + 0,0,574,584,1,0,0,0,575,576,5,89,0,0,576,581,3,118,59,0,577,578,5,33,0, + 0,578,580,3,118,59,0,579,577,1,0,0,0,580,583,1,0,0,0,581,579,1,0,0,0,581, + 582,1,0,0,0,582,585,1,0,0,0,583,581,1,0,0,0,584,575,1,0,0,0,584,585,1,0, + 0,0,585,117,1,0,0,0,586,587,3,60,30,0,587,588,5,31,0,0,588,590,1,0,0,0, + 589,586,1,0,0,0,589,590,1,0,0,0,590,591,1,0,0,0,591,592,3,60,30,0,592,119, + 1,0,0,0,593,594,5,18,0,0,594,595,3,36,18,0,595,596,5,88,0,0,596,597,3,62, + 31,0,597,121,1,0,0,0,598,599,5,17,0,0,599,602,3,54,27,0,600,601,5,28,0, + 0,601,603,3,30,15,0,602,600,1,0,0,0,602,603,1,0,0,0,603,123,1,0,0,0,59, + 135,144,162,174,183,191,197,205,207,212,219,224,235,241,249,251,262,269, + 280,283,289,301,307,317,321,326,336,344,357,361,365,372,376,383,389,396, + 404,412,420,437,448,459,464,468,473,484,489,493,507,518,532,543,546,551, + 573,581,584,589,602]; private static __ATN: ATN; public static get _ATN(): ATN { @@ -3994,8 +4132,8 @@ export class MatchBooleanExpressionContext extends ParserRuleContext { public valueExpression(): ValueExpressionContext { return this.getTypedRuleContext(ValueExpressionContext, 0) as ValueExpressionContext; } - public DEV_MATCH(): TerminalNode { - return this.getToken(esql_parser.DEV_MATCH, 0); + public MATCH(): TerminalNode { + return this.getToken(esql_parser.MATCH, 0); } public string_(): StringContext { return this.getTypedRuleContext(StringContext, 0) as StringContext; @@ -4301,8 +4439,8 @@ export class FunctionExpressionContext extends ParserRuleContext { super(parent, invokingState); this.parser = parser; } - public identifierOrParameter(): IdentifierOrParameterContext { - return this.getTypedRuleContext(IdentifierOrParameterContext, 0) as IdentifierOrParameterContext; + public functionName(): FunctionNameContext { + return this.getTypedRuleContext(FunctionNameContext, 0) as FunctionNameContext; } public LP(): TerminalNode { return this.getToken(esql_parser.LP, 0); @@ -4341,6 +4479,33 @@ export class FunctionExpressionContext extends ParserRuleContext { } +export class FunctionNameContext extends ParserRuleContext { + constructor(parser?: esql_parser, parent?: ParserRuleContext, invokingState?: number) { + super(parent, invokingState); + this.parser = parser; + } + public MATCH(): TerminalNode { + return this.getToken(esql_parser.MATCH, 0); + } + public identifierOrParameter(): IdentifierOrParameterContext { + return this.getTypedRuleContext(IdentifierOrParameterContext, 0) as IdentifierOrParameterContext; + } + public get ruleIndex(): number { + return esql_parser.RULE_functionName; + } + public enterRule(listener: esql_parserListener): void { + if(listener.enterFunctionName) { + listener.enterFunctionName(this); + } + } + public exitRule(listener: esql_parserListener): void { + if(listener.exitFunctionName) { + listener.exitFunctionName(this); + } + } +} + + export class DataTypeContext extends ParserRuleContext { constructor(parser?: esql_parser, parent?: ParserRuleContext, invokingState?: number) { super(parent, invokingState); @@ -4508,15 +4673,15 @@ export class IndexPatternContext extends ParserRuleContext { super(parent, invokingState); this.parser = parser; } + public indexString(): IndexStringContext { + return this.getTypedRuleContext(IndexStringContext, 0) as IndexStringContext; + } public clusterString(): ClusterStringContext { return this.getTypedRuleContext(ClusterStringContext, 0) as ClusterStringContext; } public COLON(): TerminalNode { return this.getToken(esql_parser.COLON, 0); } - public indexString(): IndexStringContext { - return this.getTypedRuleContext(IndexStringContext, 0) as IndexStringContext; - } public get ruleIndex(): number { return esql_parser.RULE_indexPattern; } @@ -4678,7 +4843,7 @@ export class Deprecated_metadataContext extends ParserRuleContext { export class MetricsCommandContext extends ParserRuleContext { - public _aggregates!: FieldsContext; + public _aggregates!: AggFieldsContext; public _grouping!: FieldsContext; constructor(parser?: esql_parser, parent?: ParserRuleContext, invokingState?: number) { super(parent, invokingState); @@ -4702,11 +4867,11 @@ export class MetricsCommandContext extends ParserRuleContext { public BY(): TerminalNode { return this.getToken(esql_parser.BY, 0); } - public fields_list(): FieldsContext[] { - return this.getTypedRuleContexts(FieldsContext) as FieldsContext[]; + public aggFields(): AggFieldsContext { + return this.getTypedRuleContext(AggFieldsContext, 0) as AggFieldsContext; } - public fields(i: number): FieldsContext { - return this.getTypedRuleContext(FieldsContext, i) as FieldsContext; + public fields(): FieldsContext { + return this.getTypedRuleContext(FieldsContext, 0) as FieldsContext; } public get ruleIndex(): number { return esql_parser.RULE_metricsCommand; @@ -4752,7 +4917,7 @@ export class EvalCommandContext extends ParserRuleContext { export class StatsCommandContext extends ParserRuleContext { - public _stats!: FieldsContext; + public _stats!: AggFieldsContext; public _grouping!: FieldsContext; constructor(parser?: esql_parser, parent?: ParserRuleContext, invokingState?: number) { super(parent, invokingState); @@ -4764,11 +4929,11 @@ export class StatsCommandContext extends ParserRuleContext { public BY(): TerminalNode { return this.getToken(esql_parser.BY, 0); } - public fields_list(): FieldsContext[] { - return this.getTypedRuleContexts(FieldsContext) as FieldsContext[]; + public aggFields(): AggFieldsContext { + return this.getTypedRuleContext(AggFieldsContext, 0) as AggFieldsContext; } - public fields(i: number): FieldsContext { - return this.getTypedRuleContext(FieldsContext, i) as FieldsContext; + public fields(): FieldsContext { + return this.getTypedRuleContext(FieldsContext, 0) as FieldsContext; } public get ruleIndex(): number { return esql_parser.RULE_statsCommand; @@ -4786,6 +4951,69 @@ export class StatsCommandContext extends ParserRuleContext { } +export class AggFieldsContext extends ParserRuleContext { + constructor(parser?: esql_parser, parent?: ParserRuleContext, invokingState?: number) { + super(parent, invokingState); + this.parser = parser; + } + public aggField_list(): AggFieldContext[] { + return this.getTypedRuleContexts(AggFieldContext) as AggFieldContext[]; + } + public aggField(i: number): AggFieldContext { + return this.getTypedRuleContext(AggFieldContext, i) as AggFieldContext; + } + public COMMA_list(): TerminalNode[] { + return this.getTokens(esql_parser.COMMA); + } + public COMMA(i: number): TerminalNode { + return this.getToken(esql_parser.COMMA, i); + } + public get ruleIndex(): number { + return esql_parser.RULE_aggFields; + } + public enterRule(listener: esql_parserListener): void { + if(listener.enterAggFields) { + listener.enterAggFields(this); + } + } + public exitRule(listener: esql_parserListener): void { + if(listener.exitAggFields) { + listener.exitAggFields(this); + } + } +} + + +export class AggFieldContext extends ParserRuleContext { + constructor(parser?: esql_parser, parent?: ParserRuleContext, invokingState?: number) { + super(parent, invokingState); + this.parser = parser; + } + public field(): FieldContext { + return this.getTypedRuleContext(FieldContext, 0) as FieldContext; + } + public WHERE(): TerminalNode { + return this.getToken(esql_parser.WHERE, 0); + } + public booleanExpression(): BooleanExpressionContext { + return this.getTypedRuleContext(BooleanExpressionContext, 0) as BooleanExpressionContext; + } + public get ruleIndex(): number { + return esql_parser.RULE_aggField; + } + public enterRule(listener: esql_parserListener): void { + if(listener.enterAggField) { + listener.enterAggField(this); + } + } + public exitRule(listener: esql_parserListener): void { + if(listener.exitAggField) { + listener.exitAggField(this); + } + } +} + + export class QualifiedNameContext extends ParserRuleContext { constructor(parser?: esql_parser, parent?: ParserRuleContext, invokingState?: number) { super(parent, invokingState); @@ -6037,7 +6265,7 @@ export class LookupCommandContext extends ParserRuleContext { export class InlinestatsCommandContext extends ParserRuleContext { - public _stats!: FieldsContext; + public _stats!: AggFieldsContext; public _grouping!: FieldsContext; constructor(parser?: esql_parser, parent?: ParserRuleContext, invokingState?: number) { super(parent, invokingState); @@ -6046,15 +6274,15 @@ export class InlinestatsCommandContext extends ParserRuleContext { public DEV_INLINESTATS(): TerminalNode { return this.getToken(esql_parser.DEV_INLINESTATS, 0); } - public fields_list(): FieldsContext[] { - return this.getTypedRuleContexts(FieldsContext) as FieldsContext[]; - } - public fields(i: number): FieldsContext { - return this.getTypedRuleContext(FieldsContext, i) as FieldsContext; + public aggFields(): AggFieldsContext { + return this.getTypedRuleContext(AggFieldsContext, 0) as AggFieldsContext; } public BY(): TerminalNode { return this.getToken(esql_parser.BY, 0); } + public fields(): FieldsContext { + return this.getTypedRuleContext(FieldsContext, 0) as FieldsContext; + } public get ruleIndex(): number { return esql_parser.RULE_inlinestatsCommand; } diff --git a/packages/kbn-esql-ast/src/antlr/esql_parser_listener.ts b/packages/kbn-esql-ast/src/antlr/esql_parser_listener.ts index f5c54adbe18d5..576418862be01 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_parser_listener.ts +++ b/packages/kbn-esql-ast/src/antlr/esql_parser_listener.ts @@ -38,6 +38,7 @@ import { ConstantDefaultContext } from "./esql_parser.js"; import { ParenthesizedExpressionContext } from "./esql_parser.js"; import { FunctionContext } from "./esql_parser.js"; import { FunctionExpressionContext } from "./esql_parser.js"; +import { FunctionNameContext } from "./esql_parser.js"; import { ToDataTypeContext } from "./esql_parser.js"; import { RowCommandContext } from "./esql_parser.js"; import { FieldsContext } from "./esql_parser.js"; @@ -52,6 +53,8 @@ import { Deprecated_metadataContext } from "./esql_parser.js"; import { MetricsCommandContext } from "./esql_parser.js"; import { EvalCommandContext } from "./esql_parser.js"; import { StatsCommandContext } from "./esql_parser.js"; +import { AggFieldsContext } from "./esql_parser.js"; +import { AggFieldContext } from "./esql_parser.js"; import { QualifiedNameContext } from "./esql_parser.js"; import { QualifiedNamePatternContext } from "./esql_parser.js"; import { QualifiedNamePatternsContext } from "./esql_parser.js"; @@ -400,6 +403,16 @@ export default class esql_parserListener extends ParseTreeListener { * @param ctx the parse tree */ exitFunctionExpression?: (ctx: FunctionExpressionContext) => void; + /** + * Enter a parse tree produced by `esql_parser.functionName`. + * @param ctx the parse tree + */ + enterFunctionName?: (ctx: FunctionNameContext) => void; + /** + * Exit a parse tree produced by `esql_parser.functionName`. + * @param ctx the parse tree + */ + exitFunctionName?: (ctx: FunctionNameContext) => void; /** * Enter a parse tree produced by the `toDataType` * labeled alternative in `esql_parser.dataType`. @@ -542,6 +555,26 @@ export default class esql_parserListener extends ParseTreeListener { * @param ctx the parse tree */ exitStatsCommand?: (ctx: StatsCommandContext) => void; + /** + * Enter a parse tree produced by `esql_parser.aggFields`. + * @param ctx the parse tree + */ + enterAggFields?: (ctx: AggFieldsContext) => void; + /** + * Exit a parse tree produced by `esql_parser.aggFields`. + * @param ctx the parse tree + */ + exitAggFields?: (ctx: AggFieldsContext) => void; + /** + * Enter a parse tree produced by `esql_parser.aggField`. + * @param ctx the parse tree + */ + enterAggField?: (ctx: AggFieldContext) => void; + /** + * Exit a parse tree produced by `esql_parser.aggField`. + * @param ctx the parse tree + */ + exitAggField?: (ctx: AggFieldContext) => void; /** * Enter a parse tree produced by `esql_parser.qualifiedName`. * @param ctx the parse tree diff --git a/packages/kbn-esql-ast/src/ast/helpers.ts b/packages/kbn-esql-ast/src/ast/helpers.ts index 9ca49dcb38822..74a7b5c0991e8 100644 --- a/packages/kbn-esql-ast/src/ast/helpers.ts +++ b/packages/kbn-esql-ast/src/ast/helpers.ts @@ -7,11 +7,22 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { ESQLAstNode, ESQLBinaryExpression, ESQLFunction } from '../types'; +import type { + ESQLAstNode, + ESQLBinaryExpression, + ESQLColumn, + ESQLFunction, + ESQLIntegerLiteral, + ESQLLiteral, + ESQLProperNode, +} from '../types'; import { BinaryExpressionGroup } from './constants'; +export const isProperNode = (node: unknown): node is ESQLProperNode => + !!node && typeof node === 'object' && !Array.isArray(node); + export const isFunctionExpression = (node: unknown): node is ESQLFunction => - !!node && typeof node === 'object' && !Array.isArray(node) && (node as any).type === 'function'; + isProperNode(node) && node.type === 'function'; /** * Returns true if the given node is a binary expression, i.e. an operator @@ -28,6 +39,18 @@ export const isFunctionExpression = (node: unknown): node is ESQLFunction => export const isBinaryExpression = (node: unknown): node is ESQLBinaryExpression => isFunctionExpression(node) && node.subtype === 'binary-expression'; +export const isLiteral = (node: unknown): node is ESQLLiteral => + isProperNode(node) && node.type === 'literal'; + +export const isIntegerLiteral = (node: unknown): node is ESQLIntegerLiteral => + isLiteral(node) && node.literalType === 'integer'; + +export const isDoubleLiteral = (node: unknown): node is ESQLIntegerLiteral => + isLiteral(node) && node.literalType === 'double'; + +export const isColumn = (node: unknown): node is ESQLColumn => + isProperNode(node) && node.type === 'column'; + /** * Returns the group of a binary expression: * diff --git a/packages/kbn-esql-ast/src/parser/esql_ast_builder_listener.ts b/packages/kbn-esql-ast/src/parser/esql_ast_builder_listener.ts index 3959d42d8a35f..a3f5bfabed154 100644 --- a/packages/kbn-esql-ast/src/parser/esql_ast_builder_listener.ts +++ b/packages/kbn-esql-ast/src/parser/esql_ast_builder_listener.ts @@ -45,6 +45,7 @@ import { getPosition } from './helpers'; import { collectAllSourceIdentifiers, collectAllFields, + collectAllAggFields, visitByOption, collectAllColumnIdentifiers, visitRenameClauses, @@ -144,8 +145,8 @@ export class ESQLAstBuilderListener implements ESQLParserListener { .map((sourceCtx) => createSource(sourceCtx)), }; this.ast.push(node); - const aggregates = collectAllFields(ctx.fields(0)); - const grouping = collectAllFields(ctx.fields(1)); + const aggregates = collectAllAggFields(ctx.aggFields()); + const grouping = collectAllFields(ctx.fields()); if (aggregates && aggregates.length) { node.aggregates = aggregates; } @@ -175,10 +176,10 @@ export class ESQLAstBuilderListener implements ESQLParserListener { // STATS expression is optional if (ctx._stats) { - command.args.push(...collectAllFields(ctx.fields(0))); + command.args.push(...collectAllAggFields(ctx.aggFields())); } if (ctx._grouping) { - command.args.push(...visitByOption(ctx, ctx._stats ? ctx.fields(1) : ctx.fields(0))); + command.args.push(...visitByOption(ctx, ctx.fields())); } } @@ -192,10 +193,10 @@ export class ESQLAstBuilderListener implements ESQLParserListener { // STATS expression is optional if (ctx._stats) { - command.args.push(...collectAllFields(ctx.fields(0))); + command.args.push(...collectAllAggFields(ctx.aggFields())); } if (ctx._grouping) { - command.args.push(...visitByOption(ctx, ctx._stats ? ctx.fields(1) : ctx.fields(0))); + command.args.push(...visitByOption(ctx, ctx.fields())); } } diff --git a/packages/kbn-esql-ast/src/parser/walkers.ts b/packages/kbn-esql-ast/src/parser/walkers.ts index 30c17c56483f8..df10161f68bf8 100644 --- a/packages/kbn-esql-ast/src/parser/walkers.ts +++ b/packages/kbn-esql-ast/src/parser/walkers.ts @@ -30,6 +30,7 @@ import { type EnrichCommandContext, type FieldContext, type FieldsContext, + type AggFieldsContext, type FromCommandContext, FunctionContext, type GrokCommandContext, @@ -477,8 +478,11 @@ export function visitPrimaryExpression(ctx: PrimaryExpressionContext): ESQLAstIt } if (ctx instanceof FunctionContext) { const functionExpressionCtx = ctx.functionExpression(); + const functionNameContext = functionExpressionCtx.functionName().MATCH() + ? functionExpressionCtx.functionName().MATCH() + : functionExpressionCtx.functionName().identifierOrParameter(); const fn = createFunction( - functionExpressionCtx.identifierOrParameter().getText().toLowerCase(), + functionNameContext.getText().toLowerCase(), ctx, undefined, 'variadic-call' @@ -596,6 +600,21 @@ export function visitField(ctx: FieldContext) { return collectBooleanExpression(ctx.booleanExpression()); } +export function collectAllAggFields(ctx: AggFieldsContext | undefined): ESQLAstField[] { + const ast: ESQLAstField[] = []; + if (!ctx) { + return ast; + } + try { + for (const aggField of ctx.aggField_list()) { + ast.push(...(visitField(aggField.field()) as ESQLAstField[])); + } + } catch (e) { + // do nothing + } + return ast; +} + export function collectAllFields(ctx: FieldsContext | undefined): ESQLAstField[] { const ast: ESQLAstField[] = []; if (!ctx) { diff --git a/packages/kbn-esql-ast/src/pretty_print/__tests__/basic_pretty_printer.test.ts b/packages/kbn-esql-ast/src/pretty_print/__tests__/basic_pretty_printer.test.ts index 20db9e729f094..9e21c45f75b4b 100644 --- a/packages/kbn-esql-ast/src/pretty_print/__tests__/basic_pretty_printer.test.ts +++ b/packages/kbn-esql-ast/src/pretty_print/__tests__/basic_pretty_printer.test.ts @@ -16,7 +16,7 @@ const reprint = (src: string) => { const { root } = parse(src); const text = BasicPrettyPrinter.print(root); - // console.log(JSON.stringify(ast, null, 2)); + // console.log(JSON.stringify(root, null, 2)); return { text }; }; @@ -194,6 +194,66 @@ describe('single line query', () => { expect(text).toBe('ROW NOT a'); }); + + test('negative numbers', () => { + const { text } = reprint('ROW -1'); + + expect(text).toBe('ROW -1'); + }); + + test('negative numbers in brackets', () => { + const { text } = reprint('ROW -(1)'); + + expect(text).toBe('ROW -1'); + }); + + test('negative column names', () => { + const { text } = reprint('ROW -col'); + + expect(text).toBe('ROW -col'); + }); + + test('plus unary expression', () => { + const { text } = reprint('ROW +(23)'); + + expect(text).toBe('ROW 23'); + }); + + test('chained multiple unary expressions', () => { + const { text } = reprint('ROW ----+-+(23)'); + + expect(text).toBe('ROW -23'); + }); + + test('before another expression', () => { + const { text } = reprint('ROW ----+-+(1 + 1)'); + + expect(text).toBe('ROW -(1 + 1)'); + }); + + test('negative one from the right side', () => { + const { text } = reprint('ROW 2 * -1'); + + expect(text).toBe('ROW -2'); + }); + + test('two minuses is plus', () => { + const { text } = reprint('ROW --123'); + + expect(text).toBe('ROW 123'); + }); + + test('two minuses is plus (float)', () => { + const { text } = reprint('ROW --1.23'); + + expect(text).toBe('ROW 1.23'); + }); + + test('two minuses is plus (with brackets)', () => { + const { text } = reprint('ROW --(123)'); + + expect(text).toBe('ROW 123'); + }); }); describe('postfix unary expression', () => { diff --git a/packages/kbn-esql-ast/src/pretty_print/basic_pretty_printer.ts b/packages/kbn-esql-ast/src/pretty_print/basic_pretty_printer.ts index ec744c65f636e..2f1e3439cd3a3 100644 --- a/packages/kbn-esql-ast/src/pretty_print/basic_pretty_printer.ts +++ b/packages/kbn-esql-ast/src/pretty_print/basic_pretty_printer.ts @@ -7,9 +7,18 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { binaryExpressionGroup } from '../ast/helpers'; +import { + binaryExpressionGroup, + isBinaryExpression, + isColumn, + isDoubleLiteral, + isIntegerLiteral, + isLiteral, + isProperNode, +} from '../ast/helpers'; import { ESQLAstBaseItem, ESQLAstCommand, ESQLAstQueryExpression } from '../types'; import { ESQLAstExpressionNode, Visitor } from '../visitor'; +import { resolveItem } from '../visitor/utils'; import { LeafPrinter } from './leaf_printer'; export interface BasicPrettyPrinterOptions { @@ -152,6 +161,62 @@ export class BasicPrettyPrinter { return formatted; } + protected simplifyMultiplicationByOne( + node: ESQLAstExpressionNode, + minusCount: number = 0 + ): string | undefined { + if (isBinaryExpression(node) && node.name === '*') { + let [left, right] = node.args; + left = resolveItem(left); + right = resolveItem(right); + + if (isProperNode(left) && isProperNode(right)) { + if (!!left.formatting || !!right.formatting) { + return undefined; + } + if (isIntegerLiteral(left)) { + if (left.value === 1) { + return this.simplifyMultiplicationByOne(right, minusCount); + } else if (left.value === -1) { + return this.simplifyMultiplicationByOne(right, minusCount + 1); + } + } + if (isIntegerLiteral(right)) { + if (right.value === 1) { + return this.simplifyMultiplicationByOne(left, minusCount); + } else if (right.value === -1) { + return this.simplifyMultiplicationByOne(left, minusCount + 1); + } + } + return undefined; + } else { + return undefined; + } + } + + const isNegative = minusCount % 2 === 1; + + if (isNegative && (isIntegerLiteral(node) || isDoubleLiteral(node)) && node.value < 0) { + return BasicPrettyPrinter.expression( + { + ...node, + value: Math.abs(node.value), + }, + this.opts + ); + } + + let expression = BasicPrettyPrinter.expression(node, this.opts); + const sign = isNegative ? '-' : ''; + const needsBrackets = !!sign && !isColumn(node) && !isLiteral(node); + + if (needsBrackets) { + expression = `(${expression})`; + } + + return sign ? `${sign}${expression}` : expression; + } + protected readonly visitor: Visitor = new Visitor() .on('visitExpression', (ctx) => { return ''; @@ -237,6 +302,18 @@ export class BasicPrettyPrinter { const groupLeft = binaryExpressionGroup(left); const groupRight = binaryExpressionGroup(right); + if ( + node.name === '*' && + ((isIntegerLiteral(left) && Math.abs(left.value) === 1) || + (isIntegerLiteral(right) && Math.abs(right.value) === 1)) + ) { + const formatted = this.simplifyMultiplicationByOne(node); + + if (formatted) { + return formatted; + } + } + let leftFormatted = ctx.visitArgument(0); let rightFormatted = ctx.visitArgument(1); diff --git a/packages/kbn-esql-ast/src/pretty_print/helpers.ts b/packages/kbn-esql-ast/src/pretty_print/helpers.ts index f9d9daac84e7a..1b4a75a119cb2 100644 --- a/packages/kbn-esql-ast/src/pretty_print/helpers.ts +++ b/packages/kbn-esql-ast/src/pretty_print/helpers.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { ESQLAstBaseItem, ESQLProperNode } from '../types'; +import type { ESQLAstBaseItem, ESQLProperNode } from '../types'; import { Walker } from '../walker'; export interface QueryPrettyPrintStats { diff --git a/packages/kbn-esql-ast/src/visitor/utils.ts b/packages/kbn-esql-ast/src/visitor/utils.ts index 0dc95b73cf9d7..da8544ef46c90 100644 --- a/packages/kbn-esql-ast/src/visitor/utils.ts +++ b/packages/kbn-esql-ast/src/visitor/utils.ts @@ -36,6 +36,10 @@ export const firstItem = (items: ESQLAstItem[]): ESQLSingleAstItem | undefined = } }; +export const resolveItem = (items: ESQLAstItem | ESQLAstItem[]): ESQLAstItem => { + return Array.isArray(items) ? resolveItem(items[0]) : items; +}; + /** * Returns the last normalized "single item" from the "item" list. * diff --git a/packages/kbn-esql-ast/src/walker/walker.test.ts b/packages/kbn-esql-ast/src/walker/walker.test.ts index 8dd40b1a87bd1..980e1499e62aa 100644 --- a/packages/kbn-esql-ast/src/walker/walker.test.ts +++ b/packages/kbn-esql-ast/src/walker/walker.test.ts @@ -36,10 +36,10 @@ test('can walk all functions', () => { test('can find assignment expression', () => { const query = 'METRICS source var0 = bucket(bytes, 1 hour)'; - const { ast } = parse(query); + const { root } = parse(query); const functions: ESQLFunction[] = []; - Walker.walk(ast, { + Walker.walk(root, { visitFunction: (fn) => { if (fn.name === '=') { functions.push(fn); diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.suggest.eval.test.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.suggest.eval.test.ts index cc04ccb407e6e..dcb6fe76f184b 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.suggest.eval.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.suggest.eval.test.ts @@ -363,8 +363,8 @@ describe('autocomplete.suggest', () => { // // Test suggestions for each possible param, within each signature variation, for each function for (const fn of scalarFunctionDefinitions) { // skip this fn for the moment as it's quite hard to test - // if (!['bucket', 'date_extract', 'date_diff', 'case'].includes(fn.name)) { - if (!['bucket', 'date_extract', 'date_diff', 'case'].includes(fn.name)) { + // Add match in the text when the autocomplete is ready https://github.com/elastic/kibana/issues/196995 + if (!['bucket', 'date_extract', 'date_diff', 'case', 'match'].includes(fn.name)) { test(`${fn.name}`, async () => { const testedCases = new Set(); diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts index e463902554074..deb4592428089 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts @@ -1123,7 +1123,7 @@ describe('autocomplete', () => { { filterText: '_source', text: '_source, ', command: TRIGGER_SUGGESTION_COMMAND }, ]); // no comma if there are no more fields - testSuggestions('FROM a METADATA _id, _ignored, _index, _source, _version/', [ + testSuggestions('FROM a METADATA _id, _ignored, _index, _source, _index_mode, _version/', [ { filterText: '_version', text: '_version | ', command: TRIGGER_SUGGESTION_COMMAND }, ]); }); diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/recommended_queries/templates.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/recommended_queries/templates.ts index f910d3ba05a3b..6a3226c9dcc8f 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/recommended_queries/templates.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/recommended_queries/templates.ts @@ -100,6 +100,21 @@ export const getRecommendedQueries = ({ ), queryString: `${fromCommand}\n | WHERE ${timeField} <=?_tend and ${timeField} >?_tstart\n | STATS count = COUNT(*) BY \`Over time\` = BUCKET(${timeField}, 50, ?_tstart, ?_tend) // ?_tstart and ?_tend take the values of the time picker`, }, + { + label: i18n.translate( + 'kbn-esql-validation-autocomplete.recommendedQueries.eventRate.label', + { + defaultMessage: 'Calculate the event rate', + } + ), + description: i18n.translate( + 'kbn-esql-validation-autocomplete.recommendedQueries.eventRate.description', + { + defaultMessage: 'Event rate over time', + } + ), + queryString: `${fromCommand}\n | STATS count = COUNT(*), min_timestamp = MIN(${timeField}) // MIN(dateField) finds the earliest timestamp in the dataset.\n | EVAL event_rate = count / DATE_DIFF("seconds", min_timestamp, NOW()) // Calculates the event rate by dividing the total count of events by the time difference (in seconds) between the earliest event and the current time.\n | KEEP event_rate`, + }, { label: i18n.translate( 'kbn-esql-validation-autocomplete.recommendedQueries.lastHour.label', diff --git a/packages/kbn-esql-validation-autocomplete/src/definitions/generated/scalar_functions.ts b/packages/kbn-esql-validation-autocomplete/src/definitions/generated/scalar_functions.ts index ea5f8f86e1909..2ace3e9ddc537 100644 --- a/packages/kbn-esql-validation-autocomplete/src/definitions/generated/scalar_functions.ts +++ b/packages/kbn-esql-validation-autocomplete/src/definitions/generated/scalar_functions.ts @@ -3209,6 +3209,85 @@ const ltrimDefinition: FunctionDefinition = { ], }; +// Do not edit this manually... generated by scripts/generate_function_definitions.ts +const matchDefinition: FunctionDefinition = { + type: 'eval', + name: 'match', + description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.match', { + defaultMessage: + 'Performs a match query on the specified field. Returns true if the provided query matches the row.', + }), + alias: undefined, + signatures: [ + { + params: [ + { + name: 'field', + type: 'keyword', + optional: false, + }, + { + name: 'query', + type: 'keyword', + optional: false, + }, + ], + returnType: 'boolean', + }, + { + params: [ + { + name: 'field', + type: 'keyword', + optional: false, + }, + { + name: 'query', + type: 'text', + optional: false, + }, + ], + returnType: 'boolean', + }, + { + params: [ + { + name: 'field', + type: 'text', + optional: false, + }, + { + name: 'query', + type: 'keyword', + optional: false, + }, + ], + returnType: 'boolean', + }, + { + params: [ + { + name: 'field', + type: 'text', + optional: false, + }, + { + name: 'query', + type: 'text', + optional: false, + }, + ], + returnType: 'boolean', + }, + ], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedOptions: ['by'], + validate: undefined, + examples: [ + 'from books \n| where match(author, "Faulkner")\n| keep book_no, author \n| sort book_no \n| limit 5;', + ], +}; + // Do not edit this manually... generated by scripts/generate_function_definitions.ts const mvAppendDefinition: FunctionDefinition = { type: 'eval', @@ -5770,8 +5849,6 @@ const qstrDefinition: FunctionDefinition = { defaultMessage: 'Performs a query string query. Returns true if the provided query string matches the row.', }), - ignoreAsSuggestion: true, - alias: undefined, signatures: [ { @@ -9077,6 +9154,7 @@ export const scalarFunctionDefinitions = [ logDefinition, log10Definition, ltrimDefinition, + matchDefinition, mvAppendDefinition, mvAvgDefinition, mvConcatDefinition, diff --git a/packages/kbn-esql-validation-autocomplete/src/shared/constants.ts b/packages/kbn-esql-validation-autocomplete/src/shared/constants.ts index c1942118a41e2..1a9f382d32a6d 100644 --- a/packages/kbn-esql-validation-autocomplete/src/shared/constants.ts +++ b/packages/kbn-esql-validation-autocomplete/src/shared/constants.ts @@ -15,4 +15,4 @@ export const SINGLE_TICK_REGEX = /`/g; export const DOUBLE_BACKTICK = '``'; export const SINGLE_BACKTICK = '`'; -export const METADATA_FIELDS = ['_version', '_id', '_index', '_source', '_ignored']; +export const METADATA_FIELDS = ['_version', '_id', '_index', '_source', '_ignored', '_index_mode']; diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.inlinestats.ts b/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.inlinestats.ts index a8fa55128251c..c1c7340da78f8 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.inlinestats.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.inlinestats.ts @@ -126,10 +126,10 @@ export const validationStatsCommandTestSuite = (setup: helpers.Setup) => { const { expectErrors } = await setup(); await expectErrors('from a_index | INLINESTATS doubleField=', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); await expectErrors('from a_index | INLINESTATS doubleField=5 by ', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); await expectErrors('from a_index | INLINESTATS avg(doubleField) by wrongField', [ 'Unknown column [wrongField]', @@ -186,7 +186,7 @@ export const validationStatsCommandTestSuite = (setup: helpers.Setup) => { const { expectErrors } = await setup(); await expectErrors('from a_index | INLINESTATS by ', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); }); diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.metrics.ts b/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.metrics.ts index 5384fdc136b4e..79dc4e21fe9d7 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.metrics.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.metrics.ts @@ -117,11 +117,11 @@ export const validationMetricsCommandTestSuite = (setup: helpers.Setup) => { await expectErrors('metrics a_index doubleField=', [ expect.any(String), - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); await expectErrors('metrics a_index doubleField=5 by ', [ expect.any(String), - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); }); diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.stats.ts b/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.stats.ts index c250166b88968..c499f2477e146 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.stats.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.stats.ts @@ -117,10 +117,10 @@ export const validationStatsCommandTestSuite = (setup: helpers.Setup) => { const { expectErrors } = await setup(); await expectErrors('from a_index | stats doubleField=', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); await expectErrors('from a_index | stats doubleField=5 by ', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); await expectErrors('from a_index | stats avg(doubleField) by wrongField', [ 'Unknown column [wrongField]', @@ -176,7 +176,7 @@ export const validationStatsCommandTestSuite = (setup: helpers.Setup) => { const { expectErrors } = await setup(); await expectErrors('from a_index | stats by ', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); }); diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json b/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json index 51ada18f02252..c66aaadf98df8 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json +++ b/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json @@ -223,7 +223,7 @@ { "query": "row", "error": [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, @@ -331,7 +331,7 @@ { "query": "row var = 1 in (", "error": [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", "Error: [in] function expects exactly 2 arguments, got 1." ], "warning": [] @@ -2645,7 +2645,7 @@ { "query": "from a_index | dissect", "error": [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, @@ -2740,7 +2740,7 @@ { "query": "from a_index | grok", "error": [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, @@ -3542,21 +3542,21 @@ { "query": "from a_index | where *+ doubleField", "error": [ - "SyntaxError: extraneous input '*' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: extraneous input '*' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, { "query": "from a_index | where /+ doubleField", "error": [ - "SyntaxError: extraneous input '/' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: extraneous input '/' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, { "query": "from a_index | where %+ doubleField", "error": [ - "SyntaxError: extraneous input '%' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: extraneous input '%' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, @@ -4443,7 +4443,7 @@ { "query": "from a_index | eval ", "error": [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, @@ -4486,7 +4486,7 @@ { "query": "from a_index | eval a=b, ", "error": [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", "Unknown column [b]" ], "warning": [] @@ -4513,7 +4513,7 @@ { "query": "from a_index | eval a=round(doubleField), ", "error": [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, @@ -5619,21 +5619,21 @@ { "query": "from a_index | eval *+ doubleField", "error": [ - "SyntaxError: extraneous input '*' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: extraneous input '*' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, { "query": "from a_index | eval /+ doubleField", "error": [ - "SyntaxError: extraneous input '/' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: extraneous input '/' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, { "query": "from a_index | eval %+ doubleField", "error": [ - "SyntaxError: extraneous input '%' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: extraneous input '%' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, @@ -6957,7 +6957,7 @@ { "query": "from a_index | eval not", "error": [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", "Error: [not] function expects exactly one argument, got 0." ], "warning": [] @@ -6965,7 +6965,7 @@ { "query": "from a_index | eval in", "error": [ - "SyntaxError: mismatched input 'in' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: mismatched input 'in' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, @@ -9014,7 +9014,7 @@ { "query": "from a_index | sort ", "error": [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, @@ -9033,7 +9033,7 @@ { "query": "from a_index | sort doubleField, ", "error": [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts b/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts index 5e636a941a86c..fae4ca16797cc 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts @@ -306,7 +306,7 @@ describe('validation logic', () => { describe('row', () => { testErrorsAndWarnings('row', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); testErrorsAndWarnings('row missing_column', ['Unknown column [missing_column]']); testErrorsAndWarnings('row fn()', ['Unknown function [fn]']); @@ -335,7 +335,7 @@ describe('validation logic', () => { "SyntaxError: mismatched input '' expecting '('", ]); testErrorsAndWarnings('row var = 1 in (', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", 'Error: [in] function expects exactly 2 arguments, got 1.', ]); testErrorsAndWarnings('row var = 1 not in ', [ @@ -690,7 +690,7 @@ describe('validation logic', () => { describe('dissect', () => { testErrorsAndWarnings('from a_index | dissect', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); testErrorsAndWarnings('from a_index | dissect textField', [ "SyntaxError: missing QUOTED_STRING at ''", @@ -741,7 +741,7 @@ describe('validation logic', () => { describe('grok', () => { testErrorsAndWarnings('from a_index | grok', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); testErrorsAndWarnings('from a_index | grok textField', [ "SyntaxError: missing QUOTED_STRING at ''", @@ -826,7 +826,7 @@ describe('validation logic', () => { } for (const wrongOp of ['*', '/', '%']) { testErrorsAndWarnings(`from a_index | where ${wrongOp}+ doubleField`, [ - `SyntaxError: extraneous input '${wrongOp}' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}`, + `SyntaxError: extraneous input '${wrongOp}' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}`, ]); } @@ -899,7 +899,7 @@ describe('validation logic', () => { describe('eval', () => { testErrorsAndWarnings('from a_index | eval ', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); testErrorsAndWarnings('from a_index | eval textField ', []); testErrorsAndWarnings('from a_index | eval b = textField', []); @@ -912,7 +912,7 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | eval a=b', ['Unknown column [b]']); testErrorsAndWarnings('from a_index | eval a=b, ', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", 'Unknown column [b]', ]); testErrorsAndWarnings('from a_index | eval a=round', ['Unknown column [round]']); @@ -921,7 +921,7 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | eval a=round(doubleField) ', []); testErrorsAndWarnings('from a_index | eval a=round(doubleField), ', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); testErrorsAndWarnings('from a_index | eval a=round(doubleField) + round(doubleField) ', []); testErrorsAndWarnings('from a_index | eval a=round(doubleField) + round(textField) ', [ @@ -984,7 +984,7 @@ describe('validation logic', () => { for (const wrongOp of ['*', '/', '%']) { testErrorsAndWarnings(`from a_index | eval ${wrongOp}+ doubleField`, [ - `SyntaxError: extraneous input '${wrongOp}' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}`, + `SyntaxError: extraneous input '${wrongOp}' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}`, ]); } testErrorsAndWarnings( @@ -1203,11 +1203,11 @@ describe('validation logic', () => { [] ); testErrorsAndWarnings('from a_index | eval not', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", 'Error: [not] function expects exactly one argument, got 0.', ]); testErrorsAndWarnings('from a_index | eval in', [ - "SyntaxError: mismatched input 'in' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input 'in' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); testErrorsAndWarnings('from a_index | eval textField in textField', [ @@ -1289,12 +1289,12 @@ describe('validation logic', () => { describe('sort', () => { testErrorsAndWarnings('from a_index | sort ', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); testErrorsAndWarnings('from a_index | sort "field" ', []); testErrorsAndWarnings('from a_index | sort wrongField ', ['Unknown column [wrongField]']); testErrorsAndWarnings('from a_index | sort doubleField, ', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); testErrorsAndWarnings('from a_index | sort doubleField, textField', []); for (const dir of ['desc', 'asc']) { diff --git a/packages/kbn-flot-charts/lib/jquery_flot.js b/packages/kbn-flot-charts/lib/jquery_flot.js index 3b13b317c616c..50524fd8f4926 100644 --- a/packages/kbn-flot-charts/lib/jquery_flot.js +++ b/packages/kbn-flot-charts/lib/jquery_flot.js @@ -1,8 +1,6 @@ /* JavaScript plotting library for jQuery, version 0.8.3. - Copyright (c) 2007-2014 IOLA and Ole Laursen. Licensed under the MIT license. - */ // first an inline dependency, jquery.colorhelpers.js, we inline it here @@ -29,482 +27,602 @@ Licensed under the MIT license. * V. 1.1: Fix error handling so e.g. parsing an empty string does * produce a color rather than just crashing. */ + (function($){$.color={};$.color.make=function(r,g,b,a){var o={};o.r=r||0;o.g=g||0;o.b=b||0;o.a=a!=null?a:1;o.add=function(c,d){for(var i=0;i=1){return"rgb("+[o.r,o.g,o.b].join(",")+")"}else{return"rgba("+[o.r,o.g,o.b,o.a].join(",")+")"}};o.normalize=function(){function clamp(min,value,max){return valuemax?max:value}o.r=clamp(0,parseInt(o.r),255);o.g=clamp(0,parseInt(o.g),255);o.b=clamp(0,parseInt(o.b),255);o.a=clamp(0,o.a,1);return o};o.clone=function(){return $.color.make(o.r,o.b,o.g,o.a)};return o.normalize()};$.color.extract=function(elem,css){var c;do{c=elem.css(css).toLowerCase();if(c!=""&&c!="transparent")break;elem=elem.parent()}while(elem.length&&!$.nodeName(elem.get(0),"body"));if(c=="rgba(0, 0, 0, 0)")c="transparent";return $.color.parse(c)};$.color.parse=function(str){var res,m=$.color.make;if(res=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10));if(res=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10),parseFloat(res[4]));if(res=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55);if(res=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55,parseFloat(res[4]));if(res=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str))return m(parseInt(res[1],16),parseInt(res[2],16),parseInt(res[3],16));if(res=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str))return m(parseInt(res[1]+res[1],16),parseInt(res[2]+res[2],16),parseInt(res[3]+res[3],16));var name=$.trim(str).toLowerCase();if(name=="transparent")return m(255,255,255,0);else{res=lookupColors[name]||[0,0,0];return m(res[0],res[1],res[2])}};var lookupColors={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery); // the actual Flot code +/* Javascript plotting library for jQuery, version 0.9.0-alpha. + +Copyright (c) 2007-2013 IOLA and Ole Laursen. +Licensed under the MIT license. + +*/ + (function($) { - // Cache the prototype hasOwnProperty for faster access + // A jquery-esque isNumeric method since we currently support 1.4.4 + // and $.isNumeric was introduced on in 1.7 + var isNumeric = $.isNumeric || function(obj) { + return obj - parseFloat( obj ) >= 0; + }; + + /** + * The Canvas object is a wrapper around an HTML5 tag. + * + * @constructor + * @param {string} cls List of classes to apply to the canvas. + * @param {element} container Element onto which to append the canvas. + * + * Requiring a container is a little iffy, but unfortunately canvas + * operations don't work unless the canvas is attached to the DOM. + */ + function Canvas(cls, container) { + + var element = container.children("." + cls)[0]; - var hasOwnProperty = Object.prototype.hasOwnProperty; + if (element == null) { - // A shim to provide 'detach' to jQuery versions prior to 1.4. Using a DOM - // operation produces the same effect as detach, i.e. removing the element - // without touching its jQuery data. + element = document.createElement("canvas"); + element.className = cls; - // Do not merge this into Flot 0.9, since it requires jQuery 1.4.4+. + $(element).css({ direction: "ltr", position: "absolute", left: 0, top: 0 }) + .appendTo(container); - if (!$.fn.detach) { - $.fn.detach = function() { - return this.each(function() { - if (this.parentNode) { - this.parentNode.removeChild( this ); + // If HTML5 Canvas isn't available, fall back to [Ex|Flash]canvas + + if (!element.getContext) { + if (window.G_vmlCanvasManager) { + element = window.G_vmlCanvasManager.initElement(element); + } else { + throw new Error("Canvas is not available. If you're using IE with a fall-back such as Excanvas, then there's either a mistake in your conditional include, or the page has no DOCTYPE and is rendering in Quirks Mode."); } - }); - }; + } + } + + this.element = element; + + var context = this.context = element.getContext("2d"); + + // Determine the screen's ratio of physical to device-independent + // pixels. This is the ratio between the canvas width that the browser + // advertises and the number of pixels actually present in that space. + + // The iPhone 4, for example, has a device-independent width of 320px, + // but its screen is actually 640px wide. It therefore has a pixel + // ratio of 2, while most normal devices have a ratio of 1. + + var devicePixelRatio = window.devicePixelRatio || 1, + backingStoreRatio = + context.webkitBackingStorePixelRatio || + context.mozBackingStorePixelRatio || + context.msBackingStorePixelRatio || + context.oBackingStorePixelRatio || + context.backingStorePixelRatio || 1; + + this.pixelRatio = devicePixelRatio / backingStoreRatio; + + // Size the canvas to match the internal dimensions of its container + + this.resize(container.width(), container.height()); + + // Collection of HTML div layers for text overlaid onto the canvas + + this.textContainer = null; + this.text = {}; + + // Cache of text fragments and metrics, so we can avoid expensively + // re-calculating them when the plot is re-rendered in a loop. + + this._textCache = {}; } - /////////////////////////////////////////////////////////////////////////// - // The Canvas object is a wrapper around an HTML5 tag. - // - // @constructor - // @param {string} cls List of classes to apply to the canvas. - // @param {element} container Element onto which to append the canvas. - // - // Requiring a container is a little iffy, but unfortunately canvas - // operations don't work unless the canvas is attached to the DOM. + /** + * Resizes the canvas to the given dimensions. + * + * @param {number} width New width of the canvas, in pixels. + * @param {number} width New height of the canvas, in pixels. + */ + Canvas.prototype.resize = function(width, height) { + + if (width <= 0 || height <= 0) { + throw new Error("Invalid dimensions for plot, width = " + width + ", height = " + height); + } + + var element = this.element, + context = this.context, + pixelRatio = this.pixelRatio; + + // Resize the canvas, increasing its density based on the display's + // pixel ratio; basically giving it more pixels without increasing the + // size of its element, to take advantage of the fact that retina + // displays have that many more pixels in the same advertised space. + + // Resizing should reset the state (excanvas seems to be buggy though) + + if (this.width !== width) { + element.width = width * pixelRatio; + element.style.width = width + "px"; + this.width = width; + } + + if (this.height !== height) { + element.height = height * pixelRatio; + element.style.height = height + "px"; + this.height = height; + } + + // Save the context, so we can reset in case we get replotted. The + // restore ensure that we're really back at the initial state, and + // should be safe even if we haven't saved the initial state yet. + + context.restore(); + context.save(); + + // Scale the coordinate space to match the display density; so even though we + // may have twice as many pixels, we still want lines and other drawing to + // appear at the same size; the extra pixels will just make them crisper. + + context.scale(pixelRatio, pixelRatio); + }; + + /** + * Clears the entire canvas area, not including any overlaid HTML text + */ + Canvas.prototype.clear = function() { + this.context.clearRect(0, 0, this.width, this.height); + }; + + /** + * Finishes rendering the canvas, including managing the text overlay. + */ + Canvas.prototype.render = function() { + + var cache = this._textCache; + + // For each text layer, add elements marked as active that haven't + // already been rendered, and remove those that are no longer active. + + for (var layerKey in cache) { + if (Object.prototype.hasOwnProperty.call(cache, layerKey)) { + + var layer = this.getTextLayer(layerKey), + layerCache = cache[layerKey]; + + layer.hide(); + + for (var styleKey in layerCache) { + if (Object.prototype.hasOwnProperty.call(layerCache, styleKey)) { + var styleCache = layerCache[styleKey]; + for (var angleKey in styleCache) { + if (Object.prototype.hasOwnProperty.call(styleCache, angleKey)) { + var angleCache = styleCache[angleKey]; + for (var key in angleCache) { + if (Object.prototype.hasOwnProperty.call(angleCache, key)) { + + var positions = angleCache[key].positions; + + for (var i = 0, position; position = positions[i]; i++) { + if (position.active) { + if (!position.rendered) { + layer.append(position.element); + position.rendered = true; + } + } else { + positions.splice(i--, 1); + if (position.rendered) { + position.element.detach(); + } + } + } + + if (positions.length === 0) { + delete angleCache[key]; + } + } + } + } + } + } + } + + layer.show(); + } + } + }; + + /** + * Creates (if necessary) and returns the text overlay container. + * + * @param {string} classes String of space-separated CSS classes used to + * uniquely identify the text layer. + * @return {object} The jQuery-wrapped text-layer div. + */ + Canvas.prototype.getTextLayer = function(classes) { + + var layer = this.text[classes]; + + // Create the text layer if it doesn't exist + + if (layer == null) { + + // Create the text layer container, if it doesn't exist + + if (this.textContainer == null) { + this.textContainer = $("

") + .css({ + position: "absolute", + top: 0, + left: 0, + bottom: 0, + right: 0, + "font-size": "smaller", + color: "#545454" + }) + .insertAfter(this.element); + } + + layer = this.text[classes] = $("
") + .addClass(classes) + .css({ + position: "absolute", + top: 0, + left: 0, + bottom: 0, + right: 0 + }) + .appendTo(this.textContainer); + } - function Canvas(cls, container) { + return layer; + }; - var element = container.children("." + cls)[0]; + /** + * Creates (if necessary) and returns a text info object. + * + * The object looks like this: + * + * { + * width: Width of the text's wrapper div. + * height: Height of the text's wrapper div. + * element: The jQuery-wrapped HTML div containing the text. + * positions: Array of positions at which this text is drawn. + * } + * + * The positions array contains objects that look like this: + * + * { + * active: Flag indicating whether the text should be visible. + * rendered: Flag indicating whether the text is currently visible. + * element: The jQuery-wrapped HTML div containing the text. + * x: X coordinate at which to draw the text. + * y: Y coordinate at which to draw the text. + * } + * + * Each position after the first receives a clone of the original element. + * + * The idea is that that the width, height, and general 'identity' of the + * text is constant no matter where it is placed; the placements are a + * secondary property. + * + * Canvas maintains a cache of recently-used text info objects; getTextInfo + * either returns the cached element or creates a new entry. + * + * @param {string} layer A string of space-separated CSS classes uniquely + * identifying the layer containing this text. + * @param {string} text Text string to retrieve info for. + * @param {(string|object)=} font Either a string of space-separated CSS + * classes or a font-spec object, defining the text's font and style. + * @param {number=} angle Angle at which to rotate the text, in degrees. + * @param {number=} width Maximum width of the text before it wraps. + * @return {object} a text info object. + */ + Canvas.prototype.getTextInfo = function(layer, text, font, angle, width) { + + var textStyle, layerCache, styleCache, angleCache, info; + + text = "" + text; // Cast to string in case we have a number or such + angle = (360 + (angle || 0)) % 360; // Normalize the angle to 0...359 + + // If the font is a font-spec object, generate a CSS font definition + + if (typeof font === "object") { + textStyle = font.style + " " + font.variant + " " + font.weight + " " + font.size + "px/" + font.lineHeight + "px " + font.family; + } else { + textStyle = font; + } - if (element == null) { + // Retrieve or create the caches for the text's layer, style, and angle - element = document.createElement("canvas"); - element.className = cls; + layerCache = this._textCache[layer]; + if (layerCache == null) { + layerCache = this._textCache[layer] = {}; + } - $(element).css({ direction: "ltr", position: "absolute", left: 0, top: 0 }) - .appendTo(container); + styleCache = layerCache[textStyle]; + if (styleCache == null) { + styleCache = layerCache[textStyle] = {}; + } - // If HTML5 Canvas isn't available, fall back to [Ex|Flash]canvas + angleCache = styleCache[angle]; + if (angleCache == null) { + angleCache = styleCache[angle] = {}; + } - if (!element.getContext) { - if (window.G_vmlCanvasManager) { - element = window.G_vmlCanvasManager.initElement(element); - } else { - throw new Error("Canvas is not available. If you're using IE with a fall-back such as Excanvas, then there's either a mistake in your conditional include, or the page has no DOCTYPE and is rendering in Quirks Mode."); - } - } - } + info = angleCache[text]; - this.element = element; + // If we can't find a matching element in our cache, create a new one - var context = this.context = element.getContext("2d"); + if (info == null) { - // Determine the screen's ratio of physical to device-independent - // pixels. This is the ratio between the canvas width that the browser - // advertises and the number of pixels actually present in that space. + var element = $("
").html(text) + .css({ + position: "absolute", + "max-width": width, + top: -9999 + }) + .appendTo(this.getTextLayer(layer)); - // The iPhone 4, for example, has a device-independent width of 320px, - // but its screen is actually 640px wide. It therefore has a pixel - // ratio of 2, while most normal devices have a ratio of 1. + if (typeof font === "object") { + element.css({ + font: textStyle, + color: font.color + }); + } else if (typeof font === "string") { + element.addClass(font); + } + + // Save the original dimensions of the text; we'll modify these + // later to take into account rotation, if there is any. + + var textWidth = element.outerWidth(true), + textHeight = element.outerHeight(true); + + // Apply rotation to the text using CSS3/IE matrix transforms + + // Note how we also set the element's width, as a work-around for + // the way most browsers resize the div on rotate, which may cause + // the contents to wrap differently. The extra +1 is because IE + // rounds the width differently and needs a little extra help. + + if (angle) { + + var radians = angle * Math.PI / 180, + sin = Math.sin(radians), + cos = Math.cos(radians), + a = cos.toFixed(6), // Use fixed-point so these don't + b = (-sin).toFixed(6), // show up in scientific notation + c = sin.toFixed(6), // when we add them to the string + transformRule; + + if ($.support.leadingWhitespace) { + + // The transform origin defaults to '50% 50%', producing + // blurry text on some browsers (Chrome) when the width or + // height happens to be odd, making 50% fractional. Avoid + // this by setting the origin to rounded values. + + var cx = textWidth / 2, + cy = textHeight / 2, + transformOrigin = Math.floor(cx) + "px " + Math.floor(cy) + "px"; + + // Transforms alter the div's appearance without changing + // its origin. This will make it difficult to position it + // later, since we'll be positioning the new bounding box + // with respect to the old origin. We can work around this + // by adding a translation to align the new bounding box's + // top-left corner with the origin, using the same matrix. + + // Rather than examining all four points, we can use the + // angle to figure out in advance which two points are in + // the top-left quadrant; we can then use the x-coordinate + // of the first (left-most) point and the y-coordinate of + // the second (top-most) point as the bounding box corner. + + var x, y; + if (angle < 90) { + x = Math.floor(cx * cos + cy * sin - cx); + y = Math.floor(cx * sin + cy * cos - cy); + } else if (angle < 180) { + x = Math.floor(cy * sin - cx * cos - cx); + y = Math.floor(cx * sin - cy * cos - cy); + } else if (angle < 270) { + x = Math.floor(-cx * cos - cy * sin - cx); + y = Math.floor(-cx * sin - cy * cos - cy); + } else { + x = Math.floor(cx * cos - cy * sin - cx); + y = Math.floor(cy * cos - cx * sin - cy); + } + + transformRule = "matrix(" + a + "," + c + "," + b + "," + a + "," + x + "," + y + ")"; + + element.css({ + width: textWidth + 1, + transform: transformRule, + "-o-transform": transformRule, + "-ms-transform": transformRule, + "-moz-transform": transformRule, + "-webkit-transform": transformRule, + "transform-origin": transformOrigin, + "-o-transform-origin": transformOrigin, + "-ms-transform-origin": transformOrigin, + "-moz-transform-origin": transformOrigin, + "-webkit-transform-origin": transformOrigin + }); - var devicePixelRatio = window.devicePixelRatio || 1, - backingStoreRatio = - context.webkitBackingStorePixelRatio || - context.mozBackingStorePixelRatio || - context.msBackingStorePixelRatio || - context.oBackingStorePixelRatio || - context.backingStorePixelRatio || 1; + } else { - this.pixelRatio = devicePixelRatio / backingStoreRatio; + // The IE7/8 matrix filter produces very ugly aliasing for + // text with a transparent background. Using a solid color + // greatly improves text clarity, although it does result + // in ugly boxes for plots using a non-white background. - // Size the canvas to match the internal dimensions of its container + // TODO: Instead of white use the actual background color? + // This still wouldn't solve the problem when the plot has + // a gradient background, but it would at least help. - this.resize(container.width(), container.height()); + transformRule = "progid:DXImageTransform.Microsoft.Matrix(M11=" + a + ", M12=" + b + ", M21=" + c + ", M22=" + a + ",sizingMethod='auto expand')"; - // Collection of HTML div layers for text overlaid onto the canvas + element.css({ + width: textWidth + 1, + filter: transformRule, + "-ms-filter": transformRule, + "background-color": "#fff" + }); + } - this.textContainer = null; - this.text = {}; + // Compute the final dimensions of the text's bounding box - // Cache of text fragments and metrics, so we can avoid expensively - // re-calculating them when the plot is re-rendered in a loop. + var ac = Math.abs(cos), + as = Math.abs(sin), + originalWidth = textWidth; + textWidth = Math.round(ac * textWidth + as * textHeight); + textHeight = Math.round(as * originalWidth + ac * textHeight); + } - this._textCache = {}; - } + info = angleCache[text] = { + width: textWidth, + height: textHeight, + element: element, + positions: [] + }; - // Resizes the canvas to the given dimensions. - // - // @param {number} width New width of the canvas, in pixels. - // @param {number} width New height of the canvas, in pixels. + element.detach(); + } - Canvas.prototype.resize = function(width, height) { + return info; + }; - if (width <= 0 || height <= 0) { - throw new Error("Invalid dimensions for plot, width = " + width + ", height = " + height); - } + /** + * Adds a text string to the canvas text overlay. + * + * The text isn't drawn immediately; it is marked as rendering, which will + * result in its addition to the canvas on the next render pass. + * + * @param {string} layer A string of space-separated CSS classes uniquely + * identifying the layer containing this text. + * @param {number} x X coordinate at which to draw the text. + * @param {number} y Y coordinate at which to draw the text. + * @param {string} text Text string to draw. + * @param {(string|object)=} font Either a string of space-separated CSS + * classes or a font-spec object, defining the text's font and style. + * @param {number=} angle Angle at which to rotate the text, in degrees. + * @param {number=} width Maximum width of the text before it wraps. + * @param {string=} halign Horizontal alignment of the text; either "left", + * "center" or "right". + * @param {string=} valign Vertical alignment of the text; either "top", + * "middle" or "bottom". + */ + Canvas.prototype.addText = function(layer, x, y, text, font, angle, width, halign, valign) { + + var info = this.getTextInfo(layer, text, font, angle, width), + positions = info.positions; + + // Tweak the div's position to match the text's alignment + + if (halign === "center") { + x -= info.width / 2; + } else if (halign === "right") { + x -= info.width; + } - var element = this.element, - context = this.context, - pixelRatio = this.pixelRatio; + if (valign === "middle") { + y -= info.height / 2; + } else if (valign === "bottom") { + y -= info.height; + } - // Resize the canvas, increasing its density based on the display's - // pixel ratio; basically giving it more pixels without increasing the - // size of its element, to take advantage of the fact that retina - // displays have that many more pixels in the same advertised space. + // Determine whether this text already exists at this position. + // If so, mark it for inclusion in the next render pass. - // Resizing should reset the state (excanvas seems to be buggy though) + for (var i = 0, position; position = positions[i]; i++) { + if (position.x === x && position.y === y) { + position.active = true; + return; + } + } - if (this.width != width) { - element.width = width * pixelRatio; - element.style.width = width + "px"; - this.width = width; - } + // If the text doesn't exist at this position, create a new entry - if (this.height != height) { - element.height = height * pixelRatio; - element.style.height = height + "px"; - this.height = height; - } + // For the very first position we'll re-use the original element, + // while for subsequent ones we'll clone it. - // Save the context, so we can reset in case we get replotted. The - // restore ensure that we're really back at the initial state, and - // should be safe even if we haven't saved the initial state yet. + position = { + active: true, + rendered: false, + element: positions.length ? info.element.clone() : info.element, + x: x, + y: y + }; - context.restore(); - context.save(); + positions.push(position); - // Scale the coordinate space to match the display density; so even though we - // may have twice as many pixels, we still want lines and other drawing to - // appear at the same size; the extra pixels will just make them crisper. + // Move the element to its final position within the container - context.scale(pixelRatio, pixelRatio); - }; + position.element.css({ + top: Math.round(y), + left: Math.round(x), + "text-align": halign // In case the text wraps + }); + }; - // Clears the entire canvas area, not including any overlaid HTML text - - Canvas.prototype.clear = function() { - this.context.clearRect(0, 0, this.width, this.height); - }; - - // Finishes rendering the canvas, including managing the text overlay. - - Canvas.prototype.render = function() { - - var cache = this._textCache; - - // For each text layer, add elements marked as active that haven't - // already been rendered, and remove those that are no longer active. - - for (var layerKey in cache) { - if (hasOwnProperty.call(cache, layerKey)) { - - var layer = this.getTextLayer(layerKey), - layerCache = cache[layerKey]; - - layer.hide(); - - for (var styleKey in layerCache) { - if (hasOwnProperty.call(layerCache, styleKey)) { - var styleCache = layerCache[styleKey]; - for (var key in styleCache) { - if (hasOwnProperty.call(styleCache, key)) { - - var positions = styleCache[key].positions; - - for (var i = 0, position; position = positions[i]; i++) { - if (position.active) { - if (!position.rendered) { - layer.append(position.element); - position.rendered = true; - } - } else { - positions.splice(i--, 1); - if (position.rendered) { - position.element.detach(); - } - } - } - - if (positions.length == 0) { - delete styleCache[key]; - } - } - } - } - } - - layer.show(); - } - } - }; - - // Creates (if necessary) and returns the text overlay container. - // - // @param {string} classes String of space-separated CSS classes used to - // uniquely identify the text layer. - // @return {object} The jQuery-wrapped text-layer div. - - Canvas.prototype.getTextLayer = function(classes) { - - var layer = this.text[classes]; - - // Create the text layer if it doesn't exist - - if (layer == null) { - - // Create the text layer container, if it doesn't exist - - if (this.textContainer == null) { - this.textContainer = $("
") - .css({ - position: "absolute", - top: 0, - left: 0, - bottom: 0, - right: 0, - 'font-size': "smaller", - color: "#545454" - }) - .insertAfter(this.element); - } - - layer = this.text[classes] = $("
") - .addClass(classes) - .css({ - position: "absolute", - top: 0, - left: 0, - bottom: 0, - right: 0 - }) - .appendTo(this.textContainer); - } - - return layer; - }; - - // Creates (if necessary) and returns a text info object. - // - // The object looks like this: - // - // { - // width: Width of the text's wrapper div. - // height: Height of the text's wrapper div. - // element: The jQuery-wrapped HTML div containing the text. - // positions: Array of positions at which this text is drawn. - // } - // - // The positions array contains objects that look like this: - // - // { - // active: Flag indicating whether the text should be visible. - // rendered: Flag indicating whether the text is currently visible. - // element: The jQuery-wrapped HTML div containing the text. - // x: X coordinate at which to draw the text. - // y: Y coordinate at which to draw the text. - // } - // - // Each position after the first receives a clone of the original element. - // - // The idea is that that the width, height, and general 'identity' of the - // text is constant no matter where it is placed; the placements are a - // secondary property. - // - // Canvas maintains a cache of recently-used text info objects; getTextInfo - // either returns the cached element or creates a new entry. - // - // @param {string} layer A string of space-separated CSS classes uniquely - // identifying the layer containing this text. - // @param {string} text Text string to retrieve info for. - // @param {(string|object)=} font Either a string of space-separated CSS - // classes or a font-spec object, defining the text's font and style. - // @param {number=} angle Angle at which to rotate the text, in degrees. - // Angle is currently unused, it will be implemented in the future. - // @param {number=} width Maximum width of the text before it wraps. - // @return {object} a text info object. - - Canvas.prototype.getTextInfo = function(layer, text, font, angle, width) { - - var textStyle, layerCache, styleCache, info; - - // Cast the value to a string, in case we were given a number or such - - text = "" + text; - - // If the font is a font-spec object, generate a CSS font definition - - if (typeof font === "object") { - textStyle = font.style + " " + font.variant + " " + font.weight + " " + font.size + "px/" + font.lineHeight + "px " + font.family; - } else { - textStyle = font; - } - - // Retrieve (or create) the cache for the text's layer and styles - - layerCache = this._textCache[layer]; - - if (layerCache == null) { - layerCache = this._textCache[layer] = {}; - } - - styleCache = layerCache[textStyle]; - - if (styleCache == null) { - styleCache = layerCache[textStyle] = {}; - } - - info = styleCache[text]; - - // If we can't find a matching element in our cache, create a new one - - if (info == null) { - - var element = $("
").text(text) - .css({ - position: "absolute", - 'max-width': width, - top: -9999 - }) - .appendTo(this.getTextLayer(layer)); - - if (typeof font === "object") { - element.css({ - font: textStyle, - color: font.color - }); - } else if (typeof font === "string") { - element.addClass(font); - } - - info = styleCache[text] = { - width: element.outerWidth(true), - height: element.outerHeight(true), - element: element, - positions: [] - }; - - element.detach(); - } - - return info; - }; - - // Adds a text string to the canvas text overlay. - // - // The text isn't drawn immediately; it is marked as rendering, which will - // result in its addition to the canvas on the next render pass. - // - // @param {string} layer A string of space-separated CSS classes uniquely - // identifying the layer containing this text. - // @param {number} x X coordinate at which to draw the text. - // @param {number} y Y coordinate at which to draw the text. - // @param {string} text Text string to draw. - // @param {(string|object)=} font Either a string of space-separated CSS - // classes or a font-spec object, defining the text's font and style. - // @param {number=} angle Angle at which to rotate the text, in degrees. - // Angle is currently unused, it will be implemented in the future. - // @param {number=} width Maximum width of the text before it wraps. - // @param {string=} halign Horizontal alignment of the text; either "left", - // "center" or "right". - // @param {string=} valign Vertical alignment of the text; either "top", - // "middle" or "bottom". - - Canvas.prototype.addText = function(layer, x, y, text, font, angle, width, halign, valign) { - - var info = this.getTextInfo(layer, text, font, angle, width), - positions = info.positions; - - // Tweak the div's position to match the text's alignment - - if (halign == "center") { - x -= info.width / 2; - } else if (halign == "right") { - x -= info.width; - } - - if (valign == "middle") { - y -= info.height / 2; - } else if (valign == "bottom") { - y -= info.height; - } - - // Determine whether this text already exists at this position. - // If so, mark it for inclusion in the next render pass. - - for (var i = 0, position; position = positions[i]; i++) { - if (position.x == x && position.y == y) { - position.active = true; - return; - } - } - - // If the text doesn't exist at this position, create a new entry - - // For the very first position we'll re-use the original element, - // while for subsequent ones we'll clone it. - - position = { - active: true, - rendered: false, - element: positions.length ? info.element.clone() : info.element, - x: x, - y: y - }; - - positions.push(position); - - // Move the element to its final position within the container - - position.element.css({ - top: Math.round(y), - left: Math.round(x), - 'text-align': halign // In case the text wraps - }); - }; - - // Removes one or more text strings from the canvas text overlay. - // - // If no parameters are given, all text within the layer is removed. - // - // Note that the text is not immediately removed; it is simply marked as - // inactive, which will result in its removal on the next render pass. - // This avoids the performance penalty for 'clear and redraw' behavior, - // where we potentially get rid of all text on a layer, but will likely - // add back most or all of it later, as when redrawing axes, for example. - // - // @param {string} layer A string of space-separated CSS classes uniquely - // identifying the layer containing this text. - // @param {number=} x X coordinate of the text. - // @param {number=} y Y coordinate of the text. - // @param {string=} text Text string to remove. - // @param {(string|object)=} font Either a string of space-separated CSS - // classes or a font-spec object, defining the text's font and style. - // @param {number=} angle Angle at which the text is rotated, in degrees. - // Angle is currently unused, it will be implemented in the future. - - Canvas.prototype.removeText = function(layer, x, y, text, font, angle) { - if (text == null) { - var layerCache = this._textCache[layer]; - if (layerCache != null) { - for (var styleKey in layerCache) { - if (hasOwnProperty.call(layerCache, styleKey)) { - var styleCache = layerCache[styleKey]; - for (var key in styleCache) { - if (hasOwnProperty.call(styleCache, key)) { - var positions = styleCache[key].positions; - for (var i = 0, position; position = positions[i]; i++) { - position.active = false; - } - } - } - } - } - } - } else { - var positions = this.getTextInfo(layer, text, font, angle).positions; - for (var i = 0, position; position = positions[i]; i++) { - if (position.x == x && position.y == y) { - position.active = false; - } - } - } - }; - - /////////////////////////////////////////////////////////////////////////// - // The top-level container for the entire plot. + /** + * Removes one or more text strings from the canvas text overlay. + * + * If no parameters are given, all text within the layer is removed. + * + * Note that the text is not immediately removed; it is simply marked as + * inactive, which will result in its removal on the next render pass. + * This avoids the performance penalty for 'clear and redraw' behavior, + * where we potentially get rid of all text on a layer, but will likely + * add back most or all of it later, as when redrawing axes, for example. + * + * @param {string} layer A string of space-separated CSS classes uniquely + * identifying the layer containing this text. + * @param {number=} x X coordinate of the text. + * @param {number=} y Y coordinate of the text. + * @param {string=} text Text string to remove. + * @param {(string|object)=} font Either a string of space-separated CSS + * classes or a font-spec object, defining the text's font and style. + * @param {number=} angle Angle at which the text is rotated, in degrees. + * Angle is currently unused, it will be implemented in the future. + */ + Canvas.prototype.removeText = function(layer, x, y, text, font, angle) { + var i, positions, position; + if (text == null) { + var layerCache = this._textCache[layer]; + if (layerCache != null) { + for (var styleKey in layerCache) { + if (Object.prototype.hasOwnProperty.call(layerCache, styleKey)) { + var styleCache = layerCache[styleKey]; + for (var angleKey in styleCache) { + if (Object.prototype.hasOwnProperty.call(styleCache, angleKey)) { + var angleCache = styleCache[angleKey]; + for (var key in angleCache) { + if (Object.prototype.hasOwnProperty.call(angleCache, key)) { + positions = angleCache[key].positions; + for (i = 0; position = positions[i]; i++) { + position.active = false; + } + } + } + } + } + } + } + } + } else { + positions = this.getTextInfo(layer, text, font, angle).positions; + for (i = 0; position = positions[i]; i++) { + if (position.x === x && position.y === y) { + position.active = false; + } + } + } + }; + /** + * The top-level container for the entire plot. + */ function Plot(placeholder, data_, options_, plugins) { // data is on the form: // [ series1, series2 ... ] @@ -517,7 +635,7 @@ Licensed under the MIT license. colors: ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"], legend: { show: true, - noColumns: 1, // number of columns in legend table + noColumns: 1, // number of colums in legend table labelFormatter: null, // fn: string -> string labelBoxBorderColor: "#ccc", // border color for the little label boxes container: null, // container (as jQuery object) to put legend in, null means default on top of graph @@ -528,31 +646,45 @@ Licensed under the MIT license. sorted: null // default to no legend sorting }, xaxis: { - show: null, // null = auto-detect, true = always, false = never - position: "bottom", // or "top" - mode: null, // null or "time" - font: null, // null (derived from CSS in placeholder) or object like { size: 11, lineHeight: 13, style: "italic", weight: "bold", family: "sans-serif", variant: "small-caps" } - color: null, // base color, labels, ticks - tickColor: null, // possibly different color of ticks, e.g. "rgba(0,0,0,0.15)" - transform: null, // null or f: number -> number to transform axis + + show: null, // null = auto-detect, true = always, false = never + position: "bottom", // or "top" + mode: null, // null or "time" + + color: null, // base color, labels, ticks + font: null, // null (derived from CSS in placeholder) or object like { size: 11, lineHeight: 13, style: "italic", weight: "bold", family: "sans-serif", variant: "small-caps" } + + min: null, // min. value to show, null means set automatically + max: null, // max. value to show, null means set automatically + autoscaleMargin: null, // margin in % to add if auto-setting min/max + + transform: null, // null or f: number -> number to transform axis inverseTransform: null, // if transform is set, this should be the inverse function - min: null, // min. value to show, null means set automatically - max: null, // max. value to show, null means set automatically - autoscaleMargin: null, // margin in % to add if auto-setting min/max - ticks: null, // either [1, 3] or [[1, "a"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-ticks - tickFormatter: null, // fn: number -> string - labelWidth: null, // size of tick labels in pixels - labelHeight: null, - reserveSpace: null, // whether to reserve space even if axis isn't shown - tickLength: null, // size in pixels of ticks, or "full" for whole line - alignTicksWithAxis: null, // axis number or null for no sync - tickDecimals: null, // no. of decimals, null means auto - tickSize: null, // number or [number, "unit"] - minTickSize: null // number or [number, "unit"] + + ticks: null, // either [1, 3] or [[1, "a"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-ticks + tickSize: null, // number or [number, "unit"] + minTickSize: null, // number or [number, "unit"] + tickFormatter: null, // fn: number -> string + tickDecimals: null, // no. of decimals, null means auto + + tickColor: null, // possibly different color of ticks, e.g. "rgba(0,0,0,0.15)" + tickLength: null, // size in pixels of ticks, or "full" for whole line + + tickWidth: null, // width of tick labels in pixels + tickHeight: null, // height of tick labels in pixels + tickFont: null, // null or font-spec object (see font, above) + + label: null, // null or an axis label string + labelFont: null, // null or font-spec object (see font, above) + labelPadding: 2, // spacing between the axis and its label + + reserveSpace: null, // whether to reserve space even if axis isn't shown + alignTicksWithAxis: null // axis number or null for no sync }, yaxis: { + position: "left", // or "right" autoscaleMargin: 0.02, - position: "left" // or "right" + labelPadding: 2 }, xaxes: [], yaxes: [], @@ -563,6 +695,7 @@ Licensed under the MIT license. lineWidth: 2, // in pixels fill: true, fillColor: "#ffffff", + strokeColor: null, symbol: "circle" // or callback }, lines: { @@ -614,26 +747,26 @@ Licensed under the MIT license. }, hooks: {} }, - surface = null, // the canvas for the plot itself - overlay = null, // canvas for interactive stuff on top of plot - eventHolder = null, // jQuery object that events should be bound to - ctx = null, octx = null, - xaxes = [], yaxes = [], - plotOffset = { left: 0, right: 0, top: 0, bottom: 0}, - plotWidth = 0, plotHeight = 0, - hooks = { - processOptions: [], - processRawData: [], - processDatapoints: [], - processOffset: [], - drawBackground: [], - drawSeries: [], - draw: [], - bindEvents: [], - drawOverlay: [], - shutdown: [] - }, - plot = this; + surface = null, // the canvas for the plot itself + overlay = null, // canvas for interactive stuff on top of plot + eventHolder = null, // jQuery object that events should be bound to + ctx = null, octx = null, + xaxes = [], yaxes = [], + plotOffset = { left: 0, right: 0, top: 0, bottom: 0}, + plotWidth = 0, plotHeight = 0, + hooks = { + processOptions: [], + processRawData: [], + processDatapoints: [], + processOffset: [], + drawBackground: [], + drawSeries: [], + draw: [], + bindEvents: [], + drawOverlay: [], + shutdown: [] + }, + plot = this; // public functions plot.setData = setData; @@ -652,10 +785,11 @@ Licensed under the MIT license. }; plot.getData = function () { return series; }; plot.getAxes = function () { - var res = {}, i; + var res = {}; $.each(xaxes.concat(yaxes), function (_, axis) { - if (axis) - res[axis.direction + (axis.n != 1 ? axis.n : "") + "axis"] = axis; + if (axis) { + res[axis.direction + (axis.n !== 1 ? axis.n : "") + "axis"] = axis; + } }); return res; }; @@ -674,26 +808,9 @@ Licensed under the MIT license. }; }; plot.shutdown = shutdown; - plot.destroy = function () { - shutdown(); - placeholder.removeData("plot").empty(); - - series = []; - options = null; - surface = null; - overlay = null; - eventHolder = null; - ctx = null; - octx = null; - xaxes = []; - yaxes = []; - hooks = null; - highlights = []; - plot = null; - }; plot.resize = function () { - var width = placeholder.width(), - height = placeholder.height(); + var width = placeholder.width(), + height = placeholder.height(); surface.resize(width, height); overlay.resize(width, height); }; @@ -713,8 +830,9 @@ Licensed under the MIT license. function executeHooks(hook, args) { args = [plot].concat(args); - for (var i = 0; i < hook.length; ++i) + for (var i = 0; i < hook.length; ++i) { hook[i].apply(this, args); + } } function initPlugins() { @@ -728,8 +846,9 @@ Licensed under the MIT license. for (var i = 0; i < plugins.length; ++i) { var p = plugins[i]; p.init(plot, classes); - if (p.options) + if (p.options) { $.extend(true, options, p.options); + } } } @@ -743,23 +862,29 @@ Licensed under the MIT license. // not expected behavior; avoid it by replacing them here. if (opts && opts.colors) { - options.colors = opts.colors; + options.colors = opts.colors; } - if (options.xaxis.color == null) - options.xaxis.color = $.color.parse(options.grid.color).scale('a', 0.22).toString(); - if (options.yaxis.color == null) - options.yaxis.color = $.color.parse(options.grid.color).scale('a', 0.22).toString(); + if (options.xaxis.color == null) { + options.xaxis.color = $.color.parse(options.grid.color).scale("a", 0.22).toString(); + } + if (options.yaxis.color == null) { + options.yaxis.color = $.color.parse(options.grid.color).scale("a", 0.22).toString(); + } - if (options.xaxis.tickColor == null) // grid.tickColor for back-compatibility + if (options.xaxis.tickColor == null) { // grid.tickColor for back-compatibility options.xaxis.tickColor = options.grid.tickColor || options.xaxis.color; - if (options.yaxis.tickColor == null) // grid.tickColor for back-compatibility + } + if (options.yaxis.tickColor == null) { // grid.tickColor for back-compatibility options.yaxis.tickColor = options.grid.tickColor || options.yaxis.color; + } - if (options.grid.borderColor == null) + if (options.grid.borderColor == null) { options.grid.borderColor = options.grid.color; - if (options.grid.tickColor == null) - options.grid.tickColor = $.color.parse(options.grid.color).scale('a', 0.22).toString(); + } + if (options.grid.tickColor == null) { + options.grid.tickColor = $.color.parse(options.grid.color).scale("a", 0.22).toString(); + } // Fill in defaults for axis options, including any unspecified // font-spec fields, if a font-spec was provided. @@ -768,16 +893,16 @@ Licensed under the MIT license. // since the rest of the code assumes that they exist. var i, axisOptions, axisCount, - fontSize = placeholder.css("font-size"), - fontSizeDefault = fontSize ? +fontSize.replace("px", "") : 13, fontDefaults = { style: placeholder.css("font-style"), - size: Math.round(0.8 * fontSizeDefault), + size: Math.round(0.8 * (+placeholder.css("font-size").replace("px", "") || 13)), variant: placeholder.css("font-variant"), weight: placeholder.css("font-weight"), family: placeholder.css("font-family") }; + fontDefaults.lineHeight = fontDefaults.size * 1.15; + axisCount = options.xaxes.length || 1; for (i = 0; i < axisCount; ++i) { @@ -786,17 +911,29 @@ Licensed under the MIT license. axisOptions.tickColor = axisOptions.color; } + // Compatibility with markrcote/flot-axislabels + + if (axisOptions) { + if (!axisOptions.label && axisOptions.axisLabel) { + axisOptions.label = axisOptions.axisLabel; + } + if (!axisOptions.labelPadding && axisOptions.axisLabelPadding) { + axisOptions.labelPadding = axisOptions.axisLabelPadding; + } + } + axisOptions = $.extend(true, {}, options.xaxis, axisOptions); options.xaxes[i] = axisOptions; + fontDefaults.color = axisOptions.color; if (axisOptions.font) { axisOptions.font = $.extend({}, fontDefaults, axisOptions.font); - if (!axisOptions.font.color) { - axisOptions.font.color = axisOptions.color; - } - if (!axisOptions.font.lineHeight) { - axisOptions.font.lineHeight = Math.round(axisOptions.font.size * 1.15); - } + } + if (axisOptions.tickFont || axisOptions.font) { + axisOptions.tickFont = $.extend({}, axisOptions.font || fontDefaults, axisOptions.tickFont); + } + if (axisOptions.label && (axisOptions.labelFont || axisOptions.font)) { + axisOptions.labelFont = $.extend({}, axisOptions.font || fontDefaults, axisOptions.labelFont); } } @@ -808,72 +945,83 @@ Licensed under the MIT license. axisOptions.tickColor = axisOptions.color; } + // Compatibility with markrcote/flot-axislabels + + if (axisOptions) { + if (!axisOptions.label && axisOptions.axisLabel) { + axisOptions.label = axisOptions.axisLabel; + } + if (!axisOptions.labelPadding && axisOptions.axisLabelPadding) { + axisOptions.labelPadding = axisOptions.axisLabelPadding; + } + } + axisOptions = $.extend(true, {}, options.yaxis, axisOptions); options.yaxes[i] = axisOptions; + fontDefaults.color = axisOptions.color; if (axisOptions.font) { axisOptions.font = $.extend({}, fontDefaults, axisOptions.font); - if (!axisOptions.font.color) { - axisOptions.font.color = axisOptions.color; - } - if (!axisOptions.font.lineHeight) { - axisOptions.font.lineHeight = Math.round(axisOptions.font.size * 1.15); - } + } + if (axisOptions.tickFont || axisOptions.font) { + axisOptions.tickFont = $.extend({}, axisOptions.font || fontDefaults, axisOptions.tickFont); + } + if (axisOptions.label && (axisOptions.labelFont || axisOptions.font)) { + axisOptions.labelFont = $.extend({}, axisOptions.font || fontDefaults, axisOptions.labelFont); } } // backwards compatibility, to be removed in future - if (options.xaxis.noTicks && options.xaxis.ticks == null) + if (options.xaxis.noTicks && options.xaxis.ticks == null) { options.xaxis.ticks = options.xaxis.noTicks; - if (options.yaxis.noTicks && options.yaxis.ticks == null) + } + if (options.yaxis.noTicks && options.yaxis.ticks == null) { options.yaxis.ticks = options.yaxis.noTicks; + } if (options.x2axis) { options.xaxes[1] = $.extend(true, {}, options.xaxis, options.x2axis); options.xaxes[1].position = "top"; - // Override the inherit to allow the axis to auto-scale - if (options.x2axis.min == null) { - options.xaxes[1].min = null; - } - if (options.x2axis.max == null) { - options.xaxes[1].max = null; - } } if (options.y2axis) { options.yaxes[1] = $.extend(true, {}, options.yaxis, options.y2axis); options.yaxes[1].position = "right"; - // Override the inherit to allow the axis to auto-scale - if (options.y2axis.min == null) { - options.yaxes[1].min = null; - } - if (options.y2axis.max == null) { - options.yaxes[1].max = null; - } } - if (options.grid.coloredAreas) + if (options.grid.coloredAreas) { options.grid.markings = options.grid.coloredAreas; - if (options.grid.coloredAreasColor) + } + if (options.grid.coloredAreasColor) { options.grid.markingsColor = options.grid.coloredAreasColor; - if (options.lines) + } + if (options.lines) { $.extend(true, options.series.lines, options.lines); - if (options.points) + } + if (options.points) { $.extend(true, options.series.points, options.points); - if (options.bars) + } + if (options.bars) { $.extend(true, options.series.bars, options.bars); - if (options.shadowSize != null) + } + if (options.shadowSize != null) { options.series.shadowSize = options.shadowSize; - if (options.highlightColor != null) + } + if (options.highlightColor != null) { options.series.highlightColor = options.highlightColor; + } // save options on axes for future reference - for (i = 0; i < options.xaxes.length; ++i) + for (i = 0; i < options.xaxes.length; ++i) { getOrCreateAxis(xaxes, i + 1).options = options.xaxes[i]; - for (i = 0; i < options.yaxes.length; ++i) + } + for (i = 0; i < options.yaxes.length; ++i) { getOrCreateAxis(yaxes, i + 1).options = options.yaxes[i]; + } // add hooks from options - for (var n in hooks) - if (options.hooks[n] && options.hooks[n].length) + for (var n in hooks) { + if (options.hooks[n] && options.hooks[n].length) { hooks[n] = hooks[n].concat(options.hooks[n]); + } + } executeHooks(hooks.processOptions, [options]); } @@ -896,9 +1044,9 @@ Licensed under the MIT license. $.extend(true, s, d[i]); d[i].data = s.data; - } - else + } else { s.data = d[i]; + } res.push(s); } @@ -907,10 +1055,12 @@ Licensed under the MIT license. function axisNumber(obj, coord) { var a = obj[coord + "axis"]; - if (typeof a == "object") // if we got a real axis, extract number + if (typeof a === "object") { // if we got a real axis, extract number a = a.n; - if (typeof a != "number") + } + if (!isNumeric(a)) { a = 1; // default to first axis + } return a; } @@ -924,20 +1074,24 @@ Licensed under the MIT license. var res = {}, i, axis; for (i = 0; i < xaxes.length; ++i) { axis = xaxes[i]; - if (axis && axis.used) + if (axis && axis.used) { res["x" + axis.n] = axis.c2p(pos.left); + } } for (i = 0; i < yaxes.length; ++i) { axis = yaxes[i]; - if (axis && axis.used) + if (axis && axis.used) { res["y" + axis.n] = axis.c2p(pos.top); + } } - if (res.x1 !== undefined) + if (res.x1 !== undefined) { res.x = res.x1; - if (res.y1 !== undefined) + } + if (res.y1 !== undefined) { res.y = res.y1; + } return res; } @@ -950,8 +1104,9 @@ Licensed under the MIT license. axis = xaxes[i]; if (axis && axis.used) { key = "x" + axis.n; - if (pos[key] == null && axis.n == 1) + if (pos[key] == null && axis.n === 1) { key = "x"; + } if (pos[key] != null) { res.left = axis.p2c(pos[key]); @@ -964,8 +1119,9 @@ Licensed under the MIT license. axis = yaxes[i]; if (axis && axis.used) { key = "y" + axis.n; - if (pos[key] == null && axis.n == 1) + if (pos[key] == null && axis.n === 1) { key = "y"; + } if (pos[key] != null) { res.top = axis.p2c(pos[key]); @@ -978,12 +1134,13 @@ Licensed under the MIT license. } function getOrCreateAxis(axes, number) { - if (!axes[number - 1]) + if (!axes[number - 1]) { axes[number - 1] = { n: number, // save the number for future reference - direction: axes == xaxes ? "x" : "y", - options: $.extend(true, {}, axes == xaxes ? options.xaxis : options.yaxis) + direction: axes === xaxes ? "x" : "y", + options: $.extend(true, {}, axes === xaxes ? options.xaxis : options.yaxis) }; + } return axes[number - 1]; } @@ -999,7 +1156,7 @@ Licensed under the MIT license. var sc = series[i].color; if (sc != null) { neededColors--; - if (typeof sc == "number" && sc > maxIndex) { + if (isNumeric(sc) && sc > maxIndex) { maxIndex = sc; } } @@ -1030,15 +1187,19 @@ Licensed under the MIT license. // Reset the variation after every few cycles, or else // it will end up producing only white or black colors. - if (i % colorPoolSize == 0 && i) { + if (i % colorPoolSize === 0 && i) { if (variation >= 0) { if (variation < 0.5) { variation = -variation - 0.2; - } else variation = 0; - } else variation = -variation; + } else { + variation = 0; + } + } else { + variation = -variation; + } } - colors[i] = c.scale('rgb', 1 + variation); + colors[i] = c.scale("rgb", 1 + variation); } // Finalize the series options, filling in their colors @@ -1051,20 +1212,22 @@ Licensed under the MIT license. if (s.color == null) { s.color = colors[colori].toString(); ++colori; - } - else if (typeof s.color == "number") + } else if (isNumeric(s.color)) { s.color = colors[s.color].toString(); + } // turn on lines automatically in case nothing is set if (s.lines.show == null) { var v, show = true; - for (v in s) + for (v in s) { if (s[v] && s[v].show) { show = false; break; } - if (show) + } + if (show) { s.lines.show = true; + } } // If nothing was provided for lines.zero, default it to match @@ -1084,15 +1247,15 @@ Licensed under the MIT license. var topSentry = Number.POSITIVE_INFINITY, bottomSentry = Number.NEGATIVE_INFINITY, fakeInfinity = Number.MAX_VALUE, - i, j, k, m, length, - s, points, ps, x, y, axis, val, f, p, - data, format; + i, j, k, m, s, points, ps, val, f, p, data, format; function updateAxis(axis, min, max) { - if (min < axis.datamin && min != -fakeInfinity) + if (min < axis.datamin && min !== -fakeInfinity) { axis.datamin = min; - if (max > axis.datamax && max != fakeInfinity) + } + if (max > axis.datamax && max !== fakeInfinity) { axis.datamax = max; + } } $.each(allAxes(), function (_, axis) { @@ -1105,7 +1268,6 @@ Licensed under the MIT license. for (i = 0; i < series.length; ++i) { s = series[i]; s.datapoints = { points: [] }; - executeHooks(hooks.processRawData, [ s, s.data, s.datapoints ]); } @@ -1134,8 +1296,9 @@ Licensed under the MIT license. s.datapoints.format = format; } - if (s.datapoints.pointsize != null) + if (s.datapoints.pointsize != null) { continue; // already filled in + } s.datapoints.pointsize = format.length; @@ -1157,20 +1320,23 @@ Licensed under the MIT license. if (f) { if (f.number && val != null) { val = +val; // convert to number - if (isNaN(val)) + if (isNaN(val)) { val = null; - else if (val == Infinity) + } else if (val === Infinity) { val = fakeInfinity; - else if (val == -Infinity) + } else if (val === -Infinity) { val = -fakeInfinity; + } } if (val == null) { - if (f.required) + if (f.required) { nullify = true; + } - if (f.defaultValue != null) + if (f.defaultValue != null) { val = f.defaultValue; + } } } @@ -1184,7 +1350,7 @@ Licensed under the MIT license. if (val != null) { f = format[m]; // extract min/max info - if (f.autoscale !== false) { + if (f.autoscale) { if (f.x) { updateAxis(s.xaxis, val, val); } @@ -1195,18 +1361,18 @@ Licensed under the MIT license. } points[k + m] = null; } - } - else { + } else { // a little bit of line specific stuff that // perhaps shouldn't be here, but lacking // better means... - if (insertSteps && k > 0 - && points[k - ps] != null - && points[k - ps] != points[k] - && points[k - ps + 1] != points[k + 1]) { + if (insertSteps && k > 0 && + points[k - ps] != null && + points[k - ps] !== points[k] && + points[k - ps + 1] !== points[k + 1]) { // copy the point to make room for a middle point - for (m = 0; m < ps; ++m) + for (m = 0; m < ps; ++m) { points[k + ps + m] = points[k + m]; + } // middle point has same y points[k + 1] = points[k - ps + 1]; @@ -1236,26 +1402,32 @@ Licensed under the MIT license. xmax = bottomSentry, ymax = bottomSentry; for (j = 0; j < points.length; j += ps) { - if (points[j] == null) + if (points[j] == null) { continue; + } for (m = 0; m < ps; ++m) { val = points[j + m]; f = format[m]; - if (!f || f.autoscale === false || val == fakeInfinity || val == -fakeInfinity) + if (!f || f.autoscale === false || val === fakeInfinity || val === -fakeInfinity) { continue; + } if (f.x) { - if (val < xmin) + if (val < xmin) { xmin = val; - if (val > xmax) + } + if (val > xmax) { xmax = val; + } } if (f.y) { - if (val < ymin) + if (val < ymin) { ymin = val; - if (val > ymax) + } + if (val > ymax) { ymax = val; + } } } } @@ -1265,21 +1437,23 @@ Licensed under the MIT license. var delta; switch (s.bars.align) { - case "left": - delta = 0; - break; - case "right": - delta = -s.bars.barWidth; - break; - default: - delta = -s.bars.barWidth / 2; + case "left": + delta = 0; + break; + case "right": + delta = -s.bars.barWidth; + break; + case "center": + delta = -s.bars.barWidth / 2; + break; + default: + throw new Error("Invalid bar alignment: " + s.bars.align); } if (s.bars.horizontal) { ymin += delta; ymax += delta + s.bars.barWidth; - } - else { + } else { xmin += delta; xmax += delta + s.bars.barWidth; } @@ -1290,10 +1464,12 @@ Licensed under the MIT license. } $.each(allAxes(), function (_, axis) { - if (axis.datamin == topSentry) + if (axis.datamin === topSentry) { axis.datamin = null; - if (axis.datamax == bottomSentry) + } + if (axis.datamax === bottomSentry) { axis.datamax = null; + } }); } @@ -1303,12 +1479,11 @@ Licensed under the MIT license. // from a previous plot in this container that we'll try to re-use. placeholder.css("padding", 0) // padding messes up the positioning - .children().filter(function(){ - return !$(this).hasClass("flot-overlay") && !$(this).hasClass('flot-base'); - }).remove(); + .children(":not(.flot-base,.flot-overlay)").remove(); - if (placeholder.css("position") == 'static') + if (placeholder.css("position") === "static") { placeholder.css("position", "relative"); // for positioning labels and overlay + } surface = new Canvas("flot-base", placeholder); overlay = new Canvas("flot-overlay", placeholder); // overlay canvas for interactive features @@ -1346,15 +1521,17 @@ Licensed under the MIT license. eventHolder.bind("mouseleave", onMouseLeave); } - if (options.grid.clickable) + if (options.grid.clickable) { eventHolder.click(onClick); + } executeHooks(hooks.bindEvents, [eventHolder]); } function shutdown() { - if (redrawTimeout) + if (redrawTimeout) { clearTimeout(redrawTimeout); + } eventHolder.unbind("mousemove", onMouseMove); eventHolder.unbind("mouseleave", onMouseLeave); @@ -1374,136 +1551,143 @@ Licensed under the MIT license. // precompute how much the axis is scaling a point // in canvas space - if (axis.direction == "x") { + if (axis.direction === "x") { s = axis.scale = plotWidth / Math.abs(t(axis.max) - t(axis.min)); m = Math.min(t(axis.max), t(axis.min)); - } - else { + } else { s = axis.scale = plotHeight / Math.abs(t(axis.max) - t(axis.min)); s = -s; m = Math.max(t(axis.max), t(axis.min)); } // data point to canvas coordinate - if (t == identity) // slight optimization + if (t === identity) { // slight optimization axis.p2c = function (p) { return (p - m) * s; }; - else + } else { axis.p2c = function (p) { return (t(p) - m) * s; }; + } // canvas coordinate to data point - if (!it) + if (!it) { axis.c2p = function (c) { return m + c / s; }; - else + } else { axis.c2p = function (c) { return it(m + c / s); }; + } } function measureTickLabels(axis) { var opts = axis.options, ticks = axis.ticks || [], - labelWidth = opts.labelWidth || 0, - labelHeight = opts.labelHeight || 0, - maxWidth = labelWidth || (axis.direction == "x" ? Math.floor(surface.width / (ticks.length || 1)) : null), - legacyStyles = axis.direction + "Axis " + axis.direction + axis.n + "Axis", - layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + legacyStyles, - font = opts.font || "flot-tick-label tickLabel"; + // Label width & height are deprecated; remove in 1.0! + tickWidth = opts.tickWidth || opts.labelWidth || 0, + tickHeight = opts.tickHeight || opts.labelHeight || 0, + maxWidth = tickWidth || axis.direction === "x" ? Math.floor(surface.width / (ticks.length || 1)) : null, + layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + axis.direction + "Axis " + axis.direction + axis.n + "Axis", + font = opts.tickFont || "flot-tick-label tickLabel"; for (var i = 0; i < ticks.length; ++i) { var t = ticks[i]; - if (!t.label) + if (!t.label) { continue; + } var info = surface.getTextInfo(layer, t.label, font, null, maxWidth); - labelWidth = Math.max(labelWidth, info.width); - labelHeight = Math.max(labelHeight, info.height); + tickWidth = Math.max(tickWidth, info.width); + tickHeight = Math.max(tickHeight, info.height); } - axis.labelWidth = opts.labelWidth || labelWidth; - axis.labelHeight = opts.labelHeight || labelHeight; + axis.tickWidth = opts.tickWidth || opts.labelWidth || tickWidth; + axis.tickHeight = opts.tickHeight || opts.labelHeight || tickHeight; + + // Label width/height properties are deprecated; remove in 1.0! + + axis.labelWidth = axis.tickWidth; + axis.labelHeight = axis.tickHeight; } + /////////////////////////////////////////////////////////////////////// + // Compute the axis bounding box based on the dimensions of its label + // and tick labels, then adjust the plotOffset to make room for it. + // + // This first phase only considers one dimension per axis; the other + // dimension depends on the other axes, and will be calculated later. + function allocateAxisBoxFirstPhase(axis) { - // find the bounding box of the axis by looking at label - // widths/heights and ticks, make room by diminishing the - // plotOffset; this first phase only looks at one - // dimension per axis, the other dimension depends on the - // other axes so will have to wait - - var lw = axis.labelWidth, - lh = axis.labelHeight, - pos = axis.options.position, - isXAxis = axis.direction === "x", - tickLength = axis.options.tickLength, + + var contentWidth = axis.tickWidth, + contentHeight = axis.tickHeight, + axisOptions = axis.options, + tickLength = axisOptions.tickLength, + axisPosition = axisOptions.position, axisMargin = options.grid.axisMargin, padding = options.grid.labelMargin, - innermost = true, - outermost = true, - first = true, - found = false; - - // Determine the axis's position in its direction and on its side - - $.each(isXAxis ? xaxes : yaxes, function(i, a) { - if (a && (a.show || a.reserveSpace)) { - if (a === axis) { - found = true; - } else if (a.options.position === pos) { - if (found) { - outermost = false; - } else { - innermost = false; - } - } - if (!found) { - first = false; - } - } - }); + all = axis.direction === "x" ? xaxes : yaxes, + innermost; - // The outermost axis on each side has no margin + // Determine the margin around the axis - if (outermost) { - axisMargin = 0; + var samePosition = $.grep(all, function(axis) { + return axis && axis.options.position === axisPosition && axis.reserveSpace; + }); + if ($.inArray(axis, samePosition) === samePosition.length - 1) { + axisMargin = 0; // outermost } - // The ticks for the first axis in each direction stretch across + // Determine whether the axis is the first (innermost) on its side + + innermost = $.inArray(axis, samePosition) === 0; + + // Determine the length of the tick marks if (tickLength == null) { - tickLength = first ? "full" : 5; + if (innermost) { + tickLength = "full"; + } else { + tickLength = 5; + } } - if (!isNaN(+tickLength)) + if (!isNaN(+tickLength)) { padding += +tickLength; + } - if (isXAxis) { - lh += padding; + // Measure the dimensions of the axis label, if it has one - if (pos == "bottom") { - plotOffset.bottom += lh + axisMargin; - axis.box = { top: surface.height - plotOffset.bottom, height: lh }; - } - else { - axis.box = { top: plotOffset.top + axisMargin, height: lh }; - plotOffset.top += lh + axisMargin; - } + if (axisOptions.label) { + var layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + axis.direction + "Axis " + axis.direction + axis.n + "Axis", + font = axisOptions.labelFont || "flot-axis-label axisLabels " + axis.direction + axis.n + "axisLabel", + angle = axis.direction === "x" ? 0 : axisOptions.position === "right" ? 90 : -90, + labelInfo = surface.getTextInfo(layer, axisOptions.label, font, angle); + contentWidth += labelInfo.width + axisOptions.labelPadding; + contentHeight += labelInfo.height + axisOptions.labelPadding; } - else { - lw += padding; - if (pos == "left") { - axis.box = { left: plotOffset.left + axisMargin, width: lw }; - plotOffset.left += lw + axisMargin; + // Compute the axis bounding box and update the plot bounds + + if (axis.direction === "x") { + contentHeight += padding; + if (axisPosition === "top") { + axis.box = { top: plotOffset.top + axisMargin, height: contentHeight }; + plotOffset.top += contentHeight + axisMargin; + } else { + plotOffset.bottom += contentHeight + axisMargin; + axis.box = { top: surface.height - plotOffset.bottom, height: contentHeight }; } - else { - plotOffset.right += lw + axisMargin; - axis.box = { left: surface.width - plotOffset.right, width: lw }; + } else { + contentWidth += padding; + if (axisPosition === "right") { + plotOffset.right += contentWidth + axisMargin; + axis.box = { left: surface.width - plotOffset.right, width: contentWidth }; + } else { + axis.box = { left: plotOffset.left + axisMargin, width: contentWidth }; + plotOffset.left += contentWidth + axisMargin; } } - // save for future reference - axis.position = pos; + axis.position = axisPosition; axis.tickLength = tickLength; axis.box.padding = padding; axis.innermost = innermost; @@ -1512,13 +1696,12 @@ Licensed under the MIT license. function allocateAxisBoxSecondPhase(axis) { // now that all axis boxes have been placed in one // dimension, we can set the remaining dimension coordinates - if (axis.direction == "x") { - axis.box.left = plotOffset.left - axis.labelWidth / 2; - axis.box.width = surface.width - plotOffset.left - plotOffset.right + axis.labelWidth; - } - else { - axis.box.top = plotOffset.top - axis.labelHeight / 2; - axis.box.height = surface.height - plotOffset.bottom - plotOffset.top + axis.labelHeight; + if (axis.direction === "x") { + axis.box.left = plotOffset.left - axis.tickWidth / 2; + axis.box.width = surface.width - plotOffset.left - plotOffset.right + axis.tickWidth; + } else { + axis.box.top = plotOffset.top - axis.tickHeight / 2; + axis.box.height = surface.height - plotOffset.bottom - plotOffset.top + axis.tickHeight; } } @@ -1527,95 +1710,92 @@ Licensed under the MIT license. // inside the canvas and isn't clipped off var minMargin = options.grid.minBorderMargin, - axis, i; + margins = { x: 0, y: 0 }, i; // check stuff from the plot (FIXME: this should just read // a value from the series, otherwise it's impossible to // customize) if (minMargin == null) { minMargin = 0; - for (i = 0; i < series.length; ++i) + for (i = 0; i < series.length; ++i) { minMargin = Math.max(minMargin, 2 * (series[i].points.radius + series[i].points.lineWidth/2)); + } } - var margins = { - left: minMargin, - right: minMargin, - top: minMargin, - bottom: minMargin - }; + margins.x = margins.y = Math.ceil(minMargin); // check axis labels, note we don't check the actual // labels but instead use the overall width/height to not // jump as much around with replots $.each(allAxes(), function (_, axis) { - if (axis.reserveSpace && axis.ticks && axis.ticks.length) { - if (axis.direction === "x") { - margins.left = Math.max(margins.left, axis.labelWidth / 2); - margins.right = Math.max(margins.right, axis.labelWidth / 2); - } else { - margins.bottom = Math.max(margins.bottom, axis.labelHeight / 2); - margins.top = Math.max(margins.top, axis.labelHeight / 2); - } + var dir = axis.direction; + if (axis.reserveSpace) { + margins[dir] = Math.ceil(Math.max(margins[dir], (dir === "x" ? axis.tickWidth : axis.tickHeight) / 2)); } }); - plotOffset.left = Math.ceil(Math.max(margins.left, plotOffset.left)); - plotOffset.right = Math.ceil(Math.max(margins.right, plotOffset.right)); - plotOffset.top = Math.ceil(Math.max(margins.top, plotOffset.top)); - plotOffset.bottom = Math.ceil(Math.max(margins.bottom, plotOffset.bottom)); + plotOffset.left = Math.max(margins.x, plotOffset.left); + plotOffset.right = Math.max(margins.x, plotOffset.right); + plotOffset.top = Math.max(margins.y, plotOffset.top); + plotOffset.bottom = Math.max(margins.y, plotOffset.bottom); } function setupGrid() { - var i, axes = allAxes(), showGrid = options.grid.show; + var axes = allAxes(), + showGrid = options.grid.show, + margin = options.grid.margin || 0, + i, a; // Initialize the plot's offset from the edge of the canvas - for (var a in plotOffset) { - var margin = options.grid.margin || 0; - plotOffset[a] = typeof margin == "number" ? margin : margin[a] || 0; + for (a in plotOffset) { + if (Object.prototype.hasOwnProperty.call(plotOffset, a)) { + plotOffset[a] = isNumeric(margin) ? margin : margin[a] || 0; + } } executeHooks(hooks.processOffset, [plotOffset]); // If the grid is visible, add its border width to the offset - for (var a in plotOffset) { - if(typeof(options.grid.borderWidth) == "object") { + for (a in plotOffset) { + if(typeof(options.grid.borderWidth) === "object") { plotOffset[a] += showGrid ? options.grid.borderWidth[a] : 0; - } - else { + } else { plotOffset[a] += showGrid ? options.grid.borderWidth : 0; } } + // init axes $.each(axes, function (_, axis) { - var axisOpts = axis.options; - axis.show = axisOpts.show == null ? axis.used : axisOpts.show; - axis.reserveSpace = axisOpts.reserveSpace == null ? axis.show : axisOpts.reserveSpace; + axis.show = axis.options.show; + if (axis.show == null) { + axis.show = axis.used; // by default an axis is visible if it's got data + } + + axis.reserveSpace = axis.show || axis.options.reserveSpace; + setRange(axis); }); if (showGrid) { - var allocatedAxes = $.grep(axes, function (axis) { - return axis.show || axis.reserveSpace; - }); + var allocatedAxes = $.grep(axes, function (axis) { return axis.reserveSpace; }); $.each(allocatedAxes, function (_, axis) { // make the ticks setupTickGeneration(axis); setTicks(axis); snapRangeToTicks(axis, axis.ticks); - // find labelWidth/Height for axis measureTickLabels(axis); }); // with all dimensions calculated, we can compute the // axis bounding boxes, start from the outside // (reverse order) - for (i = allocatedAxes.length - 1; i >= 0; --i) + for (i = allocatedAxes.length - 1; i >= 0; --i) { allocateAxisBoxFirstPhase(allocatedAxes[i]); + } // make sure we've got enough space for things that // might stick out @@ -1647,18 +1827,19 @@ Licensed under the MIT license. max = +(opts.max != null ? opts.max : axis.datamax), delta = max - min; - if (delta == 0.0) { + if (delta === 0.0) { // degenerate case - var widen = max == 0 ? 1 : 0.01; + var widen = max === 0 ? 1 : 0.01; - if (opts.min == null) + if (opts.min == null) { min -= widen; + } // always widen max if we couldn't widen min to ensure we // don't fall into min == max which doesn't work - if (opts.max == null || opts.min != null) + if (opts.max == null || opts.min != null) { max += widen; - } - else { + } + } else { // consider autoscaling var margin = opts.autoscaleMargin; if (margin != null) { @@ -1666,13 +1847,15 @@ Licensed under the MIT license. min -= delta * margin; // make sure we don't go below zero if all values // are positive - if (min < 0 && axis.datamin != null && axis.datamin >= 0) + if (min < 0 && axis.datamin != null && axis.datamin >= 0) { min = 0; + } } if (opts.max == null) { max += delta * margin; - if (max > 0 && axis.datamax != null && axis.datamax <= 0) + if (max > 0 && axis.datamax != null && axis.datamax <= 0) { max = 0; + } } } } @@ -1685,12 +1868,13 @@ Licensed under the MIT license. // estimate number of ticks var noTicks; - if (typeof opts.ticks == "number" && opts.ticks > 0) + if (isNumeric(opts.ticks) && opts.ticks > 0) { noTicks = opts.ticks; - else + } else { // heuristic based on the model a*sqrt(x) fitted to // some data points that seemed reasonable - noTicks = 0.3 * Math.sqrt(axis.direction == "x" ? surface.width : surface.height); + noTicks = 0.3 * Math.sqrt(axis.direction === "x" ? surface.width : surface.height); + } var delta = (axis.max - axis.min) / noTicks, dec = -Math.floor(Math.log(delta) / Math.LN10), @@ -1729,10 +1913,10 @@ Licensed under the MIT license. axis.tickDecimals = Math.max(0, maxDec != null ? maxDec : dec); axis.tickSize = opts.tickSize || size; - // Time mode was moved to a plug-in in 0.8, and since so many people use it - // we'll add an especially friendly reminder to make sure they included it. + // Time mode was moved to a plug-in in 0.8, but since so many people use this + // we'll add an especially friendly make sure they remembered to include it. - if (opts.mode == "time" && !axis.tickGenerator) { + if (opts.mode === "time" && !axis.tickGenerator) { throw new Error("Time mode requires the flot.time plugin."); } @@ -1754,43 +1938,46 @@ Licensed under the MIT license. v = start + i * axis.tickSize; ticks.push(v); ++i; - } while (v < axis.max && v != prev); + } while (v < axis.max && v !== prev); return ticks; }; - axis.tickFormatter = function (value, axis) { + axis.tickFormatter = function (value, axis) { - var factor = axis.tickDecimals ? Math.pow(10, axis.tickDecimals) : 1; - var formatted = "" + Math.round(value * factor) / factor; + var factor = axis.tickDecimals ? Math.pow(10, axis.tickDecimals) : 1; + var formatted = "" + Math.round(value * factor) / factor; - // If tickDecimals was specified, ensure that we have exactly that - // much precision; otherwise default to the value's own precision. + // If tickDecimals was specified, ensure that we have exactly that + // much precision; otherwise default to the value's own precision. - if (axis.tickDecimals != null) { - var decimal = formatted.indexOf("."); - var precision = decimal == -1 ? 0 : formatted.length - decimal - 1; - if (precision < axis.tickDecimals) { - return (precision ? formatted : formatted + ".") + ("" + factor).substr(1, axis.tickDecimals - precision); - } - } + if (axis.tickDecimals != null) { + var decimal = formatted.indexOf("."); + var precision = decimal === -1 ? 0 : formatted.length - decimal - 1; + if (precision < axis.tickDecimals) { + return (precision ? formatted : formatted + ".") + ("" + factor).substr(1, axis.tickDecimals - precision); + } + } return formatted; }; } - if ($.isFunction(opts.tickFormatter)) + if ($.isFunction(opts.tickFormatter)) { axis.tickFormatter = function (v, axis) { return "" + opts.tickFormatter(v, axis); }; + } if (opts.alignTicksWithAxis != null) { - var otherAxis = (axis.direction == "x" ? xaxes : yaxes)[opts.alignTicksWithAxis - 1]; - if (otherAxis && otherAxis.used && otherAxis != axis) { + var otherAxis = (axis.direction === "x" ? xaxes : yaxes)[opts.alignTicksWithAxis - 1]; + if (otherAxis && otherAxis.used && otherAxis !== axis) { // consider snapping min/max to outermost nice ticks var niceTicks = axis.tickGenerator(axis); if (niceTicks.length > 0) { - if (opts.min == null) + if (opts.min == null) { axis.min = Math.min(axis.min, niceTicks[0]); - if (opts.max == null && niceTicks.length > 1) + } + if (opts.max == null && niceTicks.length > 1) { axis.max = Math.max(axis.max, niceTicks[niceTicks.length - 1]); + } } axis.tickGenerator = function (axis) { @@ -1813,8 +2000,9 @@ Licensed under the MIT license. // only proceed if the tick interval rounded // with an extra decimal doesn't give us a // zero at end - if (!(ts.length > 1 && /\..*0$/.test((ts[1] - ts[0]).toFixed(extraDec)))) + if (!(ts.length > 1 && /\..*0$/.test((ts[1] - ts[0]).toFixed(extraDec)))) { axis.tickDecimals = extraDec; + } } } } @@ -1822,14 +2010,15 @@ Licensed under the MIT license. function setTicks(axis) { var oticks = axis.options.ticks, ticks = []; - if (oticks == null || (typeof oticks == "number" && oticks > 0)) + if (oticks == null || (isNumeric(oticks) && oticks > 0)) { ticks = axis.tickGenerator(axis); - else if (oticks) { - if ($.isFunction(oticks)) + } else if (oticks) { + if ($.isFunction(oticks)) { // generate the ticks ticks = oticks(axis); - else + } else { ticks = oticks; + } } // clean up/labelify the supplied ticks, copy them over @@ -1838,27 +2027,32 @@ Licensed under the MIT license. for (i = 0; i < ticks.length; ++i) { var label = null; var t = ticks[i]; - if (typeof t == "object") { + if (typeof t === "object") { v = +t[0]; - if (t.length > 1) + if (t.length > 1) { label = t[1]; - } - else + } + } else { v = +t; - if (label == null) + } + if (label == null) { label = axis.tickFormatter(v, axis); - if (!isNaN(v)) + } + if (!isNaN(v)) { axis.ticks.push({ v: v, label: label }); + } } } function snapRangeToTicks(axis, ticks) { if (axis.options.autoscaleMargin && ticks.length > 0) { // snap to ticks - if (axis.options.min == null) + if (axis.options.min == null) { axis.min = Math.min(axis.min, ticks[0].v); - if (axis.options.max == null && ticks.length > 1) + } + if (axis.options.max == null && ticks.length > 1) { axis.max = Math.max(axis.max, ticks[ticks.length - 1].v); + } } } @@ -1871,8 +2065,9 @@ Licensed under the MIT license. var grid = options.grid; // draw background, if any - if (grid.show && grid.backgroundColor) + if (grid.show && grid.backgroundColor) { drawBackground(); + } if (grid.show && !grid.aboveData) { drawGrid(); @@ -1902,10 +2097,11 @@ Licensed under the MIT license. for (var i = 0; i < axes.length; ++i) { axis = axes[i]; - if (axis.direction == coord) { + if (axis.direction === coord) { key = coord + axis.n + "axis"; - if (!ranges[key] && axis.n == 1) + if (!ranges[key] && axis.n === 1) { key = coord + "axis"; // support x1axis as xaxis + } if (ranges[key]) { from = ranges[key].from; to = ranges[key].to; @@ -1916,7 +2112,7 @@ Licensed under the MIT license. // backwards-compat stuff - to be removed in future if (!ranges[key]) { - axis = coord == "x" ? xaxes[0] : yaxes[0]; + axis = coord === "x" ? xaxes[0] : yaxes[0]; from = ranges[coord + "1"]; to = ranges[coord + "2"]; } @@ -1967,53 +2163,50 @@ Licensed under the MIT license. yrange = extractRange(m, "y"); // fill in missing - if (xrange.from == null) + if (xrange.from == null) { xrange.from = xrange.axis.min; - if (xrange.to == null) + } + if (xrange.to == null) { xrange.to = xrange.axis.max; - if (yrange.from == null) + } + if (yrange.from == null) { yrange.from = yrange.axis.min; - if (yrange.to == null) + } + if (yrange.to == null) { yrange.to = yrange.axis.max; + } // clip if (xrange.to < xrange.axis.min || xrange.from > xrange.axis.max || - yrange.to < yrange.axis.min || yrange.from > yrange.axis.max) + yrange.to < yrange.axis.min || yrange.from > yrange.axis.max) { continue; + } xrange.from = Math.max(xrange.from, xrange.axis.min); xrange.to = Math.min(xrange.to, xrange.axis.max); yrange.from = Math.max(yrange.from, yrange.axis.min); yrange.to = Math.min(yrange.to, yrange.axis.max); - var xequal = xrange.from === xrange.to, - yequal = yrange.from === yrange.to; - - if (xequal && yequal) { + if (xrange.from === xrange.to && yrange.from === yrange.to) { continue; } // then draw - xrange.from = Math.floor(xrange.axis.p2c(xrange.from)); - xrange.to = Math.floor(xrange.axis.p2c(xrange.to)); - yrange.from = Math.floor(yrange.axis.p2c(yrange.from)); - yrange.to = Math.floor(yrange.axis.p2c(yrange.to)); - - if (xequal || yequal) { - var lineWidth = m.lineWidth || options.grid.markingsLineWidth, - subPixel = lineWidth % 2 ? 0.5 : 0; + xrange.from = xrange.axis.p2c(xrange.from); + xrange.to = xrange.axis.p2c(xrange.to); + yrange.from = yrange.axis.p2c(yrange.from); + yrange.to = yrange.axis.p2c(yrange.to); + + if (xrange.from === xrange.to || yrange.from === yrange.to) { + // draw line ctx.beginPath(); ctx.strokeStyle = m.color || options.grid.markingsColor; - ctx.lineWidth = lineWidth; - if (xequal) { - ctx.moveTo(xrange.to + subPixel, yrange.from); - ctx.lineTo(xrange.to + subPixel, yrange.to); - } else { - ctx.moveTo(xrange.from, yrange.to + subPixel); - ctx.lineTo(xrange.to, yrange.to + subPixel); - } + ctx.lineWidth = m.lineWidth || options.grid.markingsLineWidth; + ctx.moveTo(xrange.from, yrange.from); + ctx.lineTo(xrange.to, yrange.to); ctx.stroke(); } else { + // fill area ctx.fillStyle = m.color || options.grid.markingsColor; ctx.fillRect(xrange.from, yrange.to, xrange.to - xrange.from, @@ -2029,25 +2222,27 @@ Licensed under the MIT license. for (var j = 0; j < axes.length; ++j) { var axis = axes[j], box = axis.box, t = axis.tickLength, x, y, xoff, yoff; - if (!axis.show || axis.ticks.length == 0) + if (!axis.show || axis.ticks.length === 0) { continue; + } ctx.lineWidth = 1; // find the edges - if (axis.direction == "x") { + if (axis.direction === "x") { x = 0; - if (t == "full") - y = (axis.position == "top" ? 0 : plotHeight); - else - y = box.top - plotOffset.top + (axis.position == "top" ? box.height : 0); - } - else { + if (t === "full") { + y = (axis.position === "top" ? 0 : plotHeight); + } else { + y = box.top - plotOffset.top + (axis.position === "top" ? box.height : 0); + } + } else { y = 0; - if (t == "full") - x = (axis.position == "left" ? 0 : plotWidth); - else - x = box.left - plotOffset.left + (axis.position == "left" ? box.width : 0); + if (t === "full") { + x = (axis.position === "left" ? 0 : plotWidth); + } else { + x = box.left - plotOffset.left + (axis.position === "left" ? box.width : 0); + } } // draw tick bar @@ -2055,13 +2250,14 @@ Licensed under the MIT license. ctx.strokeStyle = axis.options.color; ctx.beginPath(); xoff = yoff = 0; - if (axis.direction == "x") + if (axis.direction === "x") { xoff = plotWidth + 1; - else + } else { yoff = plotHeight + 1; + } - if (ctx.lineWidth == 1) { - if (axis.direction == "x") { + if (ctx.lineWidth === 1) { + if (axis.direction === "x") { y = Math.floor(y) + 0.5; } else { x = Math.floor(x) + 0.5; @@ -2083,33 +2279,36 @@ Licensed under the MIT license. xoff = yoff = 0; - if (isNaN(v) || v < axis.min || v > axis.max + if (isNaN(v) || v < axis.min || v > axis.max || ( // skip those lying on the axes if we got a border - || (t == "full" - && ((typeof bw == "object" && bw[axis.position] > 0) || bw > 0) - && (v == axis.min || v == axis.max))) + t === "full" && ((typeof bw === "object" && bw[axis.position] > 0) || bw > 0) && + (v === axis.min || v === axis.max) + )) { continue; + } - if (axis.direction == "x") { + if (axis.direction === "x") { x = axis.p2c(v); - yoff = t == "full" ? -plotHeight : t; + yoff = t === "full" ? -plotHeight : t; - if (axis.position == "top") + if (axis.position === "top") { yoff = -yoff; - } - else { + } + } else { y = axis.p2c(v); - xoff = t == "full" ? -plotWidth : t; + xoff = t === "full" ? -plotWidth : t; - if (axis.position == "left") + if (axis.position === "left") { xoff = -xoff; + } } - if (ctx.lineWidth == 1) { - if (axis.direction == "x") + if (ctx.lineWidth === 1) { + if (axis.direction === "x") { x = Math.floor(x) + 0.5; - else + } else { y = Math.floor(y) + 0.5; + } } ctx.moveTo(x, y); @@ -2125,7 +2324,7 @@ Licensed under the MIT license. // If either borderWidth or borderColor is an object, then draw the border // line by line instead of as one rectangle bc = options.grid.borderColor; - if(typeof bw == "object" || typeof bc == "object") { + if(typeof bw === "object" || typeof bc === "object") { if (typeof bw !== "object") { bw = {top: bw, right: bw, bottom: bw, left: bw}; } @@ -2168,8 +2367,7 @@ Licensed under the MIT license. ctx.lineTo(0- bw.left/2, 0); ctx.stroke(); } - } - else { + } else { ctx.lineWidth = bw; ctx.strokeStyle = options.grid.borderColor; ctx.strokeRect(-bw/2, -bw/2, plotWidth + bw, plotHeight + bw); @@ -2182,31 +2380,48 @@ Licensed under the MIT license. function drawAxisLabels() { $.each(allAxes(), function (_, axis) { + if (!axis.show || axis.ticks.length === 0) { + return; + } + var box = axis.box, - legacyStyles = axis.direction + "Axis " + axis.direction + axis.n + "Axis", - layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + legacyStyles, - font = axis.options.font || "flot-tick-label tickLabel", + axisOptions = axis.options, + layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + axis.direction + "Axis " + axis.direction + axis.n + "Axis", + labelFont = axisOptions.labelFont || "flot-axis-label axisLabels " + axis.direction + axis.n + "axisLabel", + tickFont = axisOptions.tickFont || "flot-tick-label tickLabel", tick, x, y, halign, valign; - // Remove text before checking for axis.show and ticks.length; - // otherwise plugins, like flot-tickrotor, that draw their own - // tick labels will end up with both theirs and the defaults. - surface.removeText(layer); - if (!axis.show || axis.ticks.length == 0) - return; + if (axisOptions.label) { + if (axis.direction === "x") { + if (axisOptions.position === "top") { + surface.addText(layer, box.left + box.width / 2, box.top, axisOptions.label, labelFont, 0, null, "center", "top"); + } else { + surface.addText(layer, box.left + box.width / 2, box.top + box.height, axisOptions.label, labelFont, 0, null, "center", "bottom"); + } + } else { + if (axisOptions.position === "right") { + surface.addText(layer, box.left + box.width, box.top + box.height / 2, axisOptions.label, labelFont, 90, null, "right", "middle"); + } else { + surface.addText(layer, box.left, box.top + box.height / 2, axisOptions.label, labelFont, -90, null, "left", "middle"); + } + } + } + + // Add labels for the ticks on this axis for (var i = 0; i < axis.ticks.length; ++i) { tick = axis.ticks[i]; - if (!tick.label || tick.v < axis.min || tick.v > axis.max) + if (!tick.label || tick.v < axis.min || tick.v > axis.max) { continue; + } - if (axis.direction == "x") { + if (axis.direction === "x") { halign = "center"; x = plotOffset.left + axis.p2c(tick.v); - if (axis.position == "bottom") { + if (axis.position === "bottom") { y = box.top + box.padding; } else { y = box.top + box.height - box.padding; @@ -2215,7 +2430,7 @@ Licensed under the MIT license. } else { valign = "middle"; y = plotOffset.top + axis.p2c(tick.v); - if (axis.position == "left") { + if (axis.position === "left") { x = box.left + box.width - box.padding; halign = "right"; } else { @@ -2223,18 +2438,21 @@ Licensed under the MIT license. } } - surface.addText(layer, x, y, tick.label, font, null, null, halign, valign); + surface.addText(layer, x, y, tick.label, tickFont, null, null, halign, valign); } }); } function drawSeries(series) { - if (series.lines.show) + if (series.lines.show) { drawSeriesLines(series); - if (series.bars.show) + } + if (series.bars.show) { drawSeriesBars(series); - if (series.points.show) + } + if (series.points.show) { drawSeriesPoints(series); + } } function drawSeriesLines(series) { @@ -2248,68 +2466,74 @@ Licensed under the MIT license. var x1 = points[i - ps], y1 = points[i - ps + 1], x2 = points[i], y2 = points[i + 1]; - if (x1 == null || x2 == null) + if (x1 == null || x2 == null) { continue; + } // clip with ymin if (y1 <= y2 && y1 < axisy.min) { - if (y2 < axisy.min) + if (y2 < axisy.min) { continue; // line segment is outside + } // compute new intersection point x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; y1 = axisy.min; - } - else if (y2 <= y1 && y2 < axisy.min) { - if (y1 < axisy.min) + } else if (y2 <= y1 && y2 < axisy.min) { + if (y1 < axisy.min) { continue; + } x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; y2 = axisy.min; } // clip with ymax if (y1 >= y2 && y1 > axisy.max) { - if (y2 > axisy.max) + if (y2 > axisy.max) { continue; + } x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; y1 = axisy.max; - } - else if (y2 >= y1 && y2 > axisy.max) { - if (y1 > axisy.max) + } else if (y2 >= y1 && y2 > axisy.max) { + if (y1 > axisy.max) { continue; + } x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; y2 = axisy.max; } // clip with xmin if (x1 <= x2 && x1 < axisx.min) { - if (x2 < axisx.min) + if (x2 < axisx.min) { continue; + } y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; x1 = axisx.min; - } - else if (x2 <= x1 && x2 < axisx.min) { - if (x1 < axisx.min) + } else if (x2 <= x1 && x2 < axisx.min) { + if (x1 < axisx.min) { continue; + } y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; x2 = axisx.min; } // clip with xmax if (x1 >= x2 && x1 > axisx.max) { - if (x2 > axisx.max) + if (x2 > axisx.max) { continue; + } y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; x1 = axisx.max; - } - else if (x2 >= x1 && x2 > axisx.max) { - if (x1 > axisx.max) + } else if (x2 >= x1 && x2 > axisx.max) { + if (x1 > axisx.max) { continue; + } y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; x2 = axisx.max; } - if (x1 != prevx || y1 != prevy) + if (x1 !== prevx || y1 !== prevy) { ctx.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset); + } prevx = x2; prevy = y2; @@ -2322,15 +2546,16 @@ Licensed under the MIT license. var points = datapoints.points, ps = datapoints.pointsize, bottom = Math.min(Math.max(0, axisy.min), axisy.max), - i = 0, top, areaOpen = false, + i = 0, areaOpen = false, ypos = 1, segmentStart = 0, segmentEnd = 0; // we process each segment in two turns, first forward // direction to sketch out top, then once we hit the // end we go backwards to sketch the bottom while (true) { - if (ps > 0 && i > points.length + ps) + if (ps > 0 && i > points.length + ps) { break; + } i += ps; // ps is negative if going backwards @@ -2347,7 +2572,7 @@ Licensed under the MIT license. continue; } - if (ps < 0 && i == segmentStart + ps) { + if (ps < 0 && i === segmentStart + ps) { // done with the reverse sweep ctx.fill(); areaOpen = false; @@ -2358,35 +2583,38 @@ Licensed under the MIT license. } } - if (x1 == null || x2 == null) + if (x1 == null || x2 == null) { continue; + } // clip x values // clip with xmin if (x1 <= x2 && x1 < axisx.min) { - if (x2 < axisx.min) + if (x2 < axisx.min) { continue; + } y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; x1 = axisx.min; - } - else if (x2 <= x1 && x2 < axisx.min) { - if (x1 < axisx.min) + } else if (x2 <= x1 && x2 < axisx.min) { + if (x1 < axisx.min) { continue; + } y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; x2 = axisx.min; } // clip with xmax if (x1 >= x2 && x1 > axisx.max) { - if (x2 > axisx.max) + if (x2 > axisx.max) { continue; + } y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; x1 = axisx.max; - } - else if (x2 >= x1 && x2 > axisx.max) { - if (x1 > axisx.max) + } else if (x2 >= x1 && x2 > axisx.max) { + if (x1 > axisx.max) { continue; + } y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; x2 = axisx.max; } @@ -2403,8 +2631,7 @@ Licensed under the MIT license. ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.max)); ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.max)); continue; - } - else if (y1 <= axisy.min && y2 <= axisy.min) { + } else if (y1 <= axisy.min && y2 <= axisy.min) { ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.min)); ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.min)); continue; @@ -2423,8 +2650,7 @@ Licensed under the MIT license. if (y1 <= y2 && y1 < axisy.min && y2 >= axisy.min) { x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; y1 = axisy.min; - } - else if (y2 <= y1 && y2 < axisy.min && y1 >= axisy.min) { + } else if (y2 <= y1 && y2 < axisy.min && y1 >= axisy.min) { x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; y2 = axisy.min; } @@ -2433,15 +2659,14 @@ Licensed under the MIT license. if (y1 >= y2 && y1 > axisy.max && y2 <= axisy.max) { x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; y1 = axisy.max; - } - else if (y2 >= y1 && y2 > axisy.max && y1 <= axisy.max) { + } else if (y2 >= y1 && y2 > axisy.max && y1 <= axisy.max) { x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; y2 = axisy.max; } // if the x value was changed we got a rectangle // to fill - if (x1 != x1old) { + if (x1 !== x1old) { ctx.lineTo(axisx.p2c(x1old), axisy.p2c(y1)); // it goes to (x1, y1), but we fill that below } @@ -2453,7 +2678,7 @@ Licensed under the MIT license. ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); // fill the other rectangle if it's there - if (x2 != x2old) { + if (x2 !== x2old) { ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); ctx.lineTo(axisx.p2c(x2old), axisy.p2c(y2)); } @@ -2486,8 +2711,9 @@ Licensed under the MIT license. plotLineArea(series.datapoints, series.xaxis, series.yaxis); } - if (lw > 0) + if (lw > 0) { plotLine(series.datapoints, 0, 0, series.xaxis, series.yaxis); + } ctx.restore(); } @@ -2497,16 +2723,18 @@ Licensed under the MIT license. for (var i = 0; i < points.length; i += ps) { var x = points[i], y = points[i + 1]; - if (x == null || x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) + if (x == null || x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) { continue; + } ctx.beginPath(); x = axisx.p2c(x); y = axisy.p2c(y) + offset; - if (symbol == "circle") + if (symbol === "circle") { ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false); - else + } else { symbol(ctx, x, y, radius, shadow); + } ctx.closePath(); if (fillStyle) { @@ -2530,8 +2758,9 @@ Licensed under the MIT license. // Doing the conditional here allows the shadow setting to still be // optional even with a lineWidth of 0. - if( lw == 0 ) + if( lw === 0 ) { lw = 0.0001; + } if (lw > 0 && sw > 0) { // draw shadow in two steps @@ -2547,14 +2776,14 @@ Licensed under the MIT license. } ctx.lineWidth = lw; - ctx.strokeStyle = series.color; + ctx.strokeStyle = series.points.strokeColor || series.color; plotPoints(series.datapoints, radius, getFillStyle(series.points, series.color), 0, false, series.xaxis, series.yaxis, symbol); ctx.restore(); } - function drawBar(x, y, b, barLeft, barRight, fillStyleCallback, axisx, axisy, c, horizontal, lineWidth) { + function drawBar(x, y, b, barLeft, barRight, offset, fillStyleCallback, axisx, axisy, c, horizontal, lineWidth) { var left, right, bottom, top, drawLeft, drawRight, drawTop, drawBottom, tmp; @@ -2578,8 +2807,7 @@ Licensed under the MIT license. drawLeft = true; drawRight = false; } - } - else { + } else { drawLeft = drawRight = drawTop = true; drawBottom = false; left = x + barLeft; @@ -2599,8 +2827,9 @@ Licensed under the MIT license. // clip if (right < axisx.min || left > axisx.max || - top < axisy.min || bottom > axisy.max) + top < axisy.min || bottom > axisy.max) { return; + } if (left < axisx.min) { left = axisx.min; @@ -2629,8 +2858,13 @@ Licensed under the MIT license. // fill the bar if (fillStyleCallback) { + c.beginPath(); + c.moveTo(left, bottom); + c.lineTo(left, top); + c.lineTo(right, top); + c.lineTo(right, bottom); c.fillStyle = fillStyleCallback(bottom, top); - c.fillRect(left, top, right - left, bottom - top) + c.fill(); } // draw outline @@ -2638,35 +2872,40 @@ Licensed under the MIT license. c.beginPath(); // FIXME: inline moveTo is buggy with excanvas - c.moveTo(left, bottom); - if (drawLeft) - c.lineTo(left, top); - else - c.moveTo(left, top); - if (drawTop) - c.lineTo(right, top); - else - c.moveTo(right, top); - if (drawRight) - c.lineTo(right, bottom); - else - c.moveTo(right, bottom); - if (drawBottom) - c.lineTo(left, bottom); - else - c.moveTo(left, bottom); + c.moveTo(left, bottom + offset); + if (drawLeft) { + c.lineTo(left, top + offset); + } else { + c.moveTo(left, top + offset); + } + if (drawTop) { + c.lineTo(right, top + offset); + } else { + c.moveTo(right, top + offset); + } + if (drawRight) { + c.lineTo(right, bottom + offset); + } else { + c.moveTo(right, bottom + offset); + } + if (drawBottom) { + c.lineTo(left, bottom + offset); + } else { + c.moveTo(left, bottom + offset); + } c.stroke(); } } function drawSeriesBars(series) { - function plotBars(datapoints, barLeft, barRight, fillStyleCallback, axisx, axisy) { + function plotBars(datapoints, barLeft, barRight, offset, fillStyleCallback, axisx, axisy) { var points = datapoints.points, ps = datapoints.pointsize; for (var i = 0; i < points.length; i += ps) { - if (points[i] == null) + if (points[i] == null) { continue; - drawBar(points[i], points[i + 1], points[i + 2], barLeft, barRight, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal, series.bars.lineWidth); + } + drawBar(points[i], points[i + 1], points[i + 2], barLeft, barRight, offset, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal, series.bars.lineWidth); } } @@ -2680,53 +2919,53 @@ Licensed under the MIT license. var barLeft; switch (series.bars.align) { - case "left": - barLeft = 0; - break; - case "right": - barLeft = -series.bars.barWidth; - break; - default: - barLeft = -series.bars.barWidth / 2; + case "left": + barLeft = 0; + break; + case "right": + barLeft = -series.bars.barWidth; + break; + case "center": + barLeft = -series.bars.barWidth / 2; + break; + default: + throw new Error("Invalid bar alignment: " + series.bars.align); } var fillStyleCallback = series.bars.fill ? function (bottom, top) { return getFillStyle(series.bars, series.color, bottom, top); } : null; - plotBars(series.datapoints, barLeft, barLeft + series.bars.barWidth, fillStyleCallback, series.xaxis, series.yaxis); + plotBars(series.datapoints, barLeft, barLeft + series.bars.barWidth, 0, fillStyleCallback, series.xaxis, series.yaxis); ctx.restore(); } function getFillStyle(filloptions, seriesColor, bottom, top) { var fill = filloptions.fill; - if (!fill) + if (!fill) { return null; + } - if (filloptions.fillColor) + if (filloptions.fillColor) { return getColorOrGradient(filloptions.fillColor, bottom, top, seriesColor); + } var c = $.color.parse(seriesColor); - c.a = typeof fill == "number" ? fill : 0.4; + c.a = isNumeric(fill) ? fill : 0.4; c.normalize(); return c.toString(); } function insertLegend() { - if (options.legend.container != null) { - $(options.legend.container).html(""); - } else { - placeholder.find(".legend").remove(); - } + placeholder.find(".legend").remove(); if (!options.legend.show) { return; } - var fragments = [], entries = [], rowStarted = false, - lf = options.legend.labelFormatter, s, label; + var entries = [], lf = options.legend.labelFormatter, s, label, i; // Build a list of legend entries, with each having a label and a color - for (var i = 0; i < series.length; ++i) { + for (i = 0; i < series.length; ++i) { s = series[i]; if (s.label) { label = lf ? lf(s.label, s) : s.label; @@ -2739,18 +2978,24 @@ Licensed under the MIT license. } } + // No entries implies no legend + + if (entries.length === 0) { + return; + } + // Sort the legend using either the default or a custom comparator if (options.legend.sorted) { if ($.isFunction(options.legend.sorted)) { entries.sort(options.legend.sorted); - } else if (options.legend.sorted == "reverse") { - entries.reverse(); + } else if (options.legend.sorted === "reverse") { + entries.reverse(); } else { - var ascending = options.legend.sorted != "descending"; + var ascending = options.legend.sorted !== "descending"; entries.sort(function(a, b) { - return a.label == b.label ? 0 : ( - ((a.label < b.label) != ascending ? 1 : -1) // Logical XOR + return a.label === b.label ? 0 : ( + (a.label < b.label) !== ascending ? 1 : -1 // Logical XOR ); }); } @@ -2758,63 +3003,86 @@ Licensed under the MIT license. // Generate markup for the list of entries, in their final order - for (var i = 0; i < entries.length; ++i) { + var table = $("
").css({ + "font-size": "smaller", + "color": options.grid.color + }), rowBuffer = null; + + for (i = 0; i < entries.length; ++i) { var entry = entries[i]; - if (i % options.legend.noColumns == 0) { - if (rowStarted) - fragments.push(''); - fragments.push(''); - rowStarted = true; + if (i % options.legend.noColumns === 0) { + if (rowBuffer !== null) { + table.append(rowBuffer); + } + rowBuffer = $(""); } - fragments.push( - '
' + - '' + entry.label + '' + var colorbox = $("
").css({ + "width": "4px", + "height": 0, + "border": "5px solid " + entry.color, + "overflow": "hidden" + }), + + borderbox = $("
").css({ + "border": "1px solid " + options.legend.labelBoxBorderColor, + "padding": "1px" + }); + + rowBuffer.append( + $("").addClass("legendColorBox").append(borderbox.append(colorbox)), + $("").addClass("legendLabel").html(entry.label) ); } - if (rowStarted) - fragments.push(''); + table.append(rowBuffer); - if (fragments.length == 0) - return; - - var table = '' + fragments.join("") + '
'; - if (options.legend.container != null) + if (options.legend.container != null) { $(options.legend.container).html(table); - else { - var pos = "", + } else { + var pos = {"position": "absolute"}, p = options.legend.position, m = options.legend.margin; - if (m[0] == null) + if (m[0] == null) { m = [m, m]; - if (p.charAt(0) == "n") - pos += 'top:' + (m[1] + plotOffset.top) + 'px;'; - else if (p.charAt(0) == "s") - pos += 'bottom:' + (m[1] + plotOffset.bottom) + 'px;'; - if (p.charAt(1) == "e") - pos += 'right:' + (m[0] + plotOffset.right) + 'px;'; - else if (p.charAt(1) == "w") - pos += 'left:' + (m[0] + plotOffset.left) + 'px;'; - var legend = $('
' + table.replace('style="', 'style="position:absolute;' + pos +';') + '
').appendTo(placeholder); - if (options.legend.backgroundOpacity != 0.0) { + } + if (p.charAt(0) === "n") { + pos.top = (m[1] + plotOffset.top) + "px"; + } else if (p.charAt(0) === "s") { + pos.bottom = (m[1] + plotOffset.bottom) + "px"; + } + if (p.charAt(1) === "e") { + pos.right = (m[0] + plotOffset.right) + "px"; + } else if (p.charAt(1) === "w") { + pos.left = (m[0] + plotOffset.left) + "px"; + } + var legend = $("
").addClass("legend").append(table.css(pos)).appendTo(placeholder); + if (options.legend.backgroundOpacity !== 0.0) { // put in the transparent background // separately to avoid blended labels and // label boxes var c = options.legend.backgroundColor; if (c == null) { c = options.grid.backgroundColor; - if (c && typeof c == "string") + if (c && typeof c === "string") { c = $.color.parse(c); - else - c = $.color.extract(legend, 'background-color'); + } else { + c = $.color.extract(legend, "background-color"); + } c.a = 1; c = c.toString(); } var div = legend.children(); - $('
').prependTo(legend).css('opacity', options.legend.backgroundOpacity); + + // Position also applies to this + $("
").css(pos).css({ + "width": div.width() + "px", + "height": div.height() + "px", + "background-color": c, + "opacity": options.legend.backgroundOpacity + }).prependTo(legend); } } } @@ -2829,11 +3097,12 @@ Licensed under the MIT license. function findNearbyItem(mouseX, mouseY, seriesFilter) { var maxDistance = options.grid.mouseActiveRadius, smallestDistance = maxDistance * maxDistance + 1, - item = null, foundPoint = false, i, j, ps; + item = null, i, j, ps; for (i = series.length - 1; i >= 0; --i) { - if (!seriesFilter(series[i])) + if (!seriesFilter(series[i])) { continue; + } var s = series[i], axisx = s.xaxis, @@ -2842,27 +3111,35 @@ Licensed under the MIT license. mx = axisx.c2p(mouseX), // precompute some stuff to make the loop faster my = axisy.c2p(mouseY), maxx = maxDistance / axisx.scale, - maxy = maxDistance / axisy.scale; + maxy = maxDistance / axisy.scale, + x, y; ps = s.datapoints.pointsize; // with inverse transforms, we can't use the maxx/maxy // optimization, sadly - if (axisx.options.inverseTransform) + if (axisx.options.inverseTransform) { maxx = Number.MAX_VALUE; - if (axisy.options.inverseTransform) + } + if (axisy.options.inverseTransform) { maxy = Number.MAX_VALUE; + } if (s.lines.show || s.points.show) { for (j = 0; j < points.length; j += ps) { - var x = points[j], y = points[j + 1]; - if (x == null) + + x = points[j]; + y = points[j + 1]; + + if (x == null) { continue; + } // For points and lines, the cursor must be within a // certain distance to the data point if (x - mx > maxx || x - mx < -maxx || - y - my > maxy || y - my < -maxy) + y - my > maxy || y - my < -maxy) { continue; + } // We have to calculate distances in pixels, not in // data units, because the scales of the axes may be different @@ -2880,34 +3157,25 @@ Licensed under the MIT license. } if (s.bars.show && !item) { // no other point can be nearby - - var barLeft, barRight; - - switch (s.bars.align) { - case "left": - barLeft = 0; - break; - case "right": - barLeft = -s.bars.barWidth; - break; - default: - barLeft = -s.bars.barWidth / 2; - } - - barRight = barLeft + s.bars.barWidth; + var barLeft = s.bars.align === "left" ? 0 : -s.bars.barWidth/2, + barRight = barLeft + s.bars.barWidth; for (j = 0; j < points.length; j += ps) { - var x = points[j], y = points[j + 1], b = points[j + 2]; - if (x == null) + x = points[j]; + y = points[j + 1]; + var b = points[j + 2]; + if (x == null) { continue; + } // for a bar graph, the cursor must be inside the bar if (series[i].bars.horizontal ? (mx <= Math.max(b, x) && mx >= Math.min(b, x) && my >= y + barLeft && my <= y + barRight) : (mx >= x + barLeft && mx <= x + barRight && - my >= Math.min(b, y) && my <= Math.max(b, y))) - item = [i, j / ps]; + my >= Math.min(b, y) && my <= Math.max(b, y))) { + item = [i, j / ps]; + } } } } @@ -2927,20 +3195,22 @@ Licensed under the MIT license. } function onMouseMove(e) { - if (options.grid.hoverable) + if (options.grid.hoverable) { triggerClickHoverEvent("plothover", e, - function (s) { return s["hoverable"] != false; }); + function (s) { return s.hoverable !== false; }); + } } function onMouseLeave(e) { - if (options.grid.hoverable) + if (options.grid.hoverable) { triggerClickHoverEvent("plothover", e, - function (s) { return false; }); + function () { return false; }); + } } function onClick(e) { triggerClickHoverEvent("plotclick", e, - function (s) { return s["clickable"] != false; }); + function (s) { return s.clickable !== false; }); } // trigger click or hover event (they send the same parameters @@ -2966,15 +3236,18 @@ Licensed under the MIT license. // clear auto-highlights for (var i = 0; i < highlights.length; ++i) { var h = highlights[i]; - if (h.auto == eventname && - !(item && h.series == item.series && - h.point[0] == item.datapoint[0] && - h.point[1] == item.datapoint[1])) + if (h.auto === eventname && !( + item && h.series === item.series && + h.point[0] === item.datapoint[0] && + h.point[1] === item.datapoint[1] + )) { unhighlight(h.series, h.point); + } } - if (item) + if (item) { highlight(item.series, item.datapoint, eventname); + } } placeholder.trigger(eventname, [ pos, item ]); @@ -2982,13 +3255,14 @@ Licensed under the MIT license. function triggerRedrawOverlay() { var t = options.interaction.redrawOverlayInterval; - if (t == -1) { // skip event queue + if (t === -1) { // skip event queue drawOverlay(); return; } - if (!redrawTimeout) + if (!redrawTimeout) { redrawTimeout = setTimeout(drawOverlay, t); + } } function drawOverlay() { @@ -3003,10 +3277,11 @@ Licensed under the MIT license. for (i = 0; i < highlights.length; ++i) { hi = highlights[i]; - if (hi.series.bars.show) + if (hi.series.bars.show) { drawBarHighlight(hi.series, hi.point); - else + } else { drawPointHighlight(hi.series, hi.point); + } } octx.restore(); @@ -3014,22 +3289,22 @@ Licensed under the MIT license. } function highlight(s, point, auto) { - if (typeof s == "number") + if (isNumeric(s)) { s = series[s]; + } - if (typeof point == "number") { + if (isNumeric(point)) { var ps = s.datapoints.pointsize; point = s.datapoints.points.slice(ps * point, ps * (point + 1)); } var i = indexOfHighlight(s, point); - if (i == -1) { + if (i === -1) { highlights.push({ series: s, point: point, auto: auto }); - triggerRedrawOverlay(); - } - else if (!auto) + } else if (!auto) { highlights[i].auto = false; + } } function unhighlight(s, point) { @@ -3039,18 +3314,18 @@ Licensed under the MIT license. return; } - if (typeof s == "number") + if (isNumeric(s)) { s = series[s]; + } - if (typeof point == "number") { + if (isNumeric(point)) { var ps = s.datapoints.pointsize; point = s.datapoints.points.slice(ps * point, ps * (point + 1)); } var i = indexOfHighlight(s, point); - if (i != -1) { + if (i !== -1) { highlights.splice(i, 1); - triggerRedrawOverlay(); } } @@ -3058,9 +3333,9 @@ Licensed under the MIT license. function indexOfHighlight(s, p) { for (var i = 0; i < highlights.length; ++i) { var h = highlights[i]; - if (h.series == s && h.point[0] == p[0] - && h.point[1] == p[1]) + if (h.series === s && h.point[0] === p[0] && h.point[1] === p[1]) { return i; + } } return -1; } @@ -3068,54 +3343,52 @@ Licensed under the MIT license. function drawPointHighlight(series, point) { var x = point[0], y = point[1], axisx = series.xaxis, axisy = series.yaxis, - highlightColor = (typeof series.highlightColor === "string") ? series.highlightColor : $.color.parse(series.color).scale('a', 0.5).toString(); + highlightColor = (typeof series.highlightColor === "string") ? series.highlightColor : $.color.parse(series.color).scale("a", 0.5).toString(); - if (x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) + if (x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) { return; + } - var pointRadius = series.points.radius + series.points.lineWidth / 2; + var pointRadius; + var radius; + if (series.points.show) { + pointRadius = series.points.radius + series.points.lineWidth / 2; + radius = 1.5 * pointRadius; + } else { + pointRadius = series.points.radius; + radius = 0.5 * pointRadius; + } octx.lineWidth = pointRadius; octx.strokeStyle = highlightColor; - var radius = 1.5 * pointRadius; x = axisx.p2c(x); y = axisy.p2c(y); octx.beginPath(); - if (series.points.symbol == "circle") + if (series.points.symbol === "circle") { octx.arc(x, y, radius, 0, 2 * Math.PI, false); - else + } else { series.points.symbol(octx, x, y, radius, false); + } octx.closePath(); octx.stroke(); } function drawBarHighlight(series, point) { - var highlightColor = (typeof series.highlightColor === "string") ? series.highlightColor : $.color.parse(series.color).scale('a', 0.5).toString(), + var highlightColor = (typeof series.highlightColor === "string") ? series.highlightColor : $.color.parse(series.color).scale("a", 0.5).toString(), fillStyle = highlightColor, - barLeft; - - switch (series.bars.align) { - case "left": - barLeft = 0; - break; - case "right": - barLeft = -series.bars.barWidth; - break; - default: - barLeft = -series.bars.barWidth / 2; - } + barLeft = series.bars.align === "left" ? 0 : -series.bars.barWidth/2; octx.lineWidth = series.bars.lineWidth; octx.strokeStyle = highlightColor; drawBar(point[0], point[1], point[2] || 0, barLeft, barLeft + series.bars.barWidth, - function () { return fillStyle; }, series.xaxis, series.yaxis, octx, series.bars.horizontal, series.bars.lineWidth); + 0, function () { return fillStyle; }, series.xaxis, series.yaxis, octx, series.bars.horizontal, series.bars.lineWidth); } function getColorOrGradient(spec, bottom, top, defaultColor) { - if (typeof spec == "string") + if (typeof spec === "string") { return spec; - else { + } else { // assume this is a gradient spec; IE currently only // supports a simple vertical gradient properly, so that's // what we support too @@ -3123,12 +3396,14 @@ Licensed under the MIT license. for (var i = 0, l = spec.colors.length; i < l; ++i) { var c = spec.colors[i]; - if (typeof c != "string") { + if (typeof c !== "string") { var co = $.color.parse(defaultColor); - if (c.brightness != null) - co = co.scale('rgb', c.brightness); - if (c.opacity != null) + if (c.brightness != null) { + co = co.scale("rgb", c.brightness); + } + if (c.opacity != null) { co.a *= c.opacity; + } c = co.toString(); } gradient.addColorStop(i / (l - 1), c); @@ -3148,7 +3423,7 @@ Licensed under the MIT license. return plot; }; - $.plot.version = "0.8.3"; + $.plot.version = "0.9.0-alpha"; $.plot.plugins = []; @@ -3165,4 +3440,4 @@ Licensed under the MIT license. return base * Math.floor(n / base); } -})(jQuery); +})(jQuery); \ No newline at end of file diff --git a/packages/kbn-generate-csv/src/generate_csv.ts b/packages/kbn-generate-csv/src/generate_csv.ts index cafc6600f51a3..5ed92df84c581 100644 --- a/packages/kbn-generate-csv/src/generate_csv.ts +++ b/packages/kbn-generate-csv/src/generate_csv.ts @@ -26,10 +26,12 @@ import { byteSizeValueToNumber, CancellationToken, ReportingError, + ReportingSavedObjectNotFoundError, } from '@kbn/reporting-common'; import type { TaskInstanceFields, TaskRunResult } from '@kbn/reporting-common/types'; import type { ReportingConfigType } from '@kbn/reporting-server'; +import { TaskErrorSource, createTaskRunError } from '@kbn/task-manager-plugin/server'; import { CONTENT_TYPE_CSV } from '../constants'; import type { JobParamsCSV } from '../types'; import { getExportSettings, type CsvExportSettings } from './lib/get_export_settings'; @@ -235,6 +237,21 @@ export class CsvGenerator { public async generateData(): Promise { const logger = this.logger; + + const createSearchSource = async () => { + try { + const source = await this.dependencies.searchSourceStart.create(this.job.searchSource); + return source; + } catch (err) { + // Saved object not found + if (err?.output?.statusCode === 404) { + const reportingError = new ReportingSavedObjectNotFoundError(err); + throw createTaskRunError(reportingError, TaskErrorSource.USER); + } + throw err; + } + }; + const [settings, searchSource] = await Promise.all([ getExportSettings( this.clients.uiSettings, @@ -243,7 +260,7 @@ export class CsvGenerator { this.job.browserTimezone, logger ), - this.dependencies.searchSourceStart.create(this.job.searchSource), + createSearchSource(), ]); const { startedAt, retryAt } = this.taskInstanceFields; diff --git a/packages/kbn-generate-csv/tsconfig.json b/packages/kbn-generate-csv/tsconfig.json index b57990c20eb4a..4216438b6689a 100644 --- a/packages/kbn-generate-csv/tsconfig.json +++ b/packages/kbn-generate-csv/tsconfig.json @@ -30,5 +30,6 @@ "@kbn/es-types", "@kbn/data-views-plugin", "@kbn/search-types", + "@kbn/task-manager-plugin", ] } diff --git a/packages/kbn-generate/src/commands/codeowners_command.ts b/packages/kbn-generate/src/commands/codeowners_command.ts index 79f7025b99a02..a86b4250d6850 100644 --- a/packages/kbn-generate/src/commands/codeowners_command.ts +++ b/packages/kbn-generate/src/commands/codeowners_command.ts @@ -63,7 +63,11 @@ export const CodeownersCommand: GenerateCommand = { } const newCodeowners = `${GENERATED_START}${pkgs - .map((pkg) => `${pkg.normalizedRepoRelativeDir} ${pkg.manifest.owner.join(' ')}`) + .map( + (pkg) => + pkg.normalizedRepoRelativeDir + + (pkg.manifest.owner.length ? ' ' + pkg.manifest.owner.join(' ') : '') + ) .join('\n')}${GENERATED_END}${content}${ULTIMATE_PRIORITY_RULES}`; if (newCodeowners === oldCodeowners) { diff --git a/packages/kbn-grid-layout/grid/grid_height_smoother.tsx b/packages/kbn-grid-layout/grid/grid_height_smoother.tsx index 960fe4f52e735..7693fac72918a 100644 --- a/packages/kbn-grid-layout/grid/grid_height_smoother.tsx +++ b/packages/kbn-grid-layout/grid/grid_height_smoother.tsx @@ -24,7 +24,7 @@ export const GridHeightSmoother = ({ gridLayoutStateManager.interactionEvent$, ]).subscribe(([dimensions, interactionEvent]) => { if (!smoothHeightRef.current) return; - if (!interactionEvent) { + if (!interactionEvent || interactionEvent.type === 'drop') { smoothHeightRef.current.style.height = `${dimensions.height}px`; return; } diff --git a/packages/kbn-grid-layout/grid/grid_layout.tsx b/packages/kbn-grid-layout/grid/grid_layout.tsx index 7587e57e9dd1c..c6bbd94dabe56 100644 --- a/packages/kbn-grid-layout/grid/grid_layout.tsx +++ b/packages/kbn-grid-layout/grid/grid_layout.tsx @@ -7,10 +7,11 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing'; import React from 'react'; + +import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing'; + import { GridHeightSmoother } from './grid_height_smoother'; -import { GridOverlay } from './grid_overlay'; import { GridRow } from './grid_row'; import { GridLayoutData, GridSettings } from './types'; import { useGridLayoutEvents } from './use_grid_layout_events'; @@ -49,17 +50,17 @@ export const GridLayout = ({ key={rowData.title} rowIndex={rowIndex} runtimeSettings={runtimeSettings} - activePanelId={interactionEvent?.id} renderPanelContents={renderPanelContents} targetRowIndex={interactionEvent?.targetRowIndex} + gridLayoutStateManager={gridLayoutStateManager} toggleIsCollapsed={() => { const currentLayout = gridLayoutStateManager.gridLayout$.value; currentLayout[rowIndex].isCollapsed = !currentLayout[rowIndex].isCollapsed; gridLayoutStateManager.gridLayout$.next(currentLayout); }} setInteractionEvent={(nextInteractionEvent) => { - if (!nextInteractionEvent) { - gridLayoutStateManager.hideDragPreview(); + if (nextInteractionEvent?.type === 'drop') { + gridLayoutStateManager.activePanel$.next(undefined); } gridLayoutStateManager.interactionEvent$.next(nextInteractionEvent); }} @@ -69,10 +70,6 @@ export const GridLayout = ({ })} - ); }; diff --git a/packages/kbn-grid-layout/grid/grid_overlay.tsx b/packages/kbn-grid-layout/grid/grid_overlay.tsx deleted file mode 100644 index 958618d2f8f38..0000000000000 --- a/packages/kbn-grid-layout/grid/grid_overlay.tsx +++ /dev/null @@ -1,135 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { EuiPortal, EuiText, transparentize } from '@elastic/eui'; -import { css } from '@emotion/react'; -import { i18n } from '@kbn/i18n'; -import { euiThemeVars } from '@kbn/ui-theme'; -import React, { useRef, useState } from 'react'; -import { GridLayoutStateManager, PanelInteractionEvent } from './types'; - -type ScrollDirection = 'up' | 'down'; - -const scrollLabels: { [key in ScrollDirection]: string } = { - up: i18n.translate('kbnGridLayout.overlays.scrollUpLabel', { defaultMessage: 'Scroll up' }), - down: i18n.translate('kbnGridLayout.overlays.scrollDownLabel', { defaultMessage: 'Scroll down' }), -}; - -const scrollOnInterval = (direction: ScrollDirection) => { - const interval = setInterval(() => { - window.scroll({ - top: window.scrollY + (direction === 'down' ? 50 : -50), - behavior: 'smooth', - }); - }, 100); - return interval; -}; - -const ScrollOnHover = ({ direction, hide }: { hide: boolean; direction: ScrollDirection }) => { - const [isActive, setIsActive] = useState(false); - const scrollInterval = useRef(null); - const stopScrollInterval = () => { - if (scrollInterval.current) { - clearInterval(scrollInterval.current); - } - }; - - return ( -
{ - setIsActive(true); - scrollInterval.current = scrollOnInterval(direction); - }} - onMouseLeave={() => { - setIsActive(false); - stopScrollInterval(); - }} - css={css` - width: 100%; - position: fixed; - display: flex; - align-items: center; - flex-direction: column; - justify-content: center; - opacity: ${hide ? 0 : 1}; - transition: opacity 100ms linear; - padding: ${euiThemeVars.euiSizeM}; - ${direction === 'down' ? 'bottom: 0;' : 'top: 0;'} - `} - > - {direction === 'up' && ( -
- )} -
- - {scrollLabels[direction]} - -
-
- ); -}; - -export const GridOverlay = ({ - interactionEvent, - gridLayoutStateManager, -}: { - interactionEvent?: PanelInteractionEvent; - gridLayoutStateManager: GridLayoutStateManager; -}) => { - return ( - -
- - -
-
- - ); -}; diff --git a/packages/kbn-grid-layout/grid/grid_panel.tsx b/packages/kbn-grid-layout/grid/grid_panel.tsx index 7bad4d1af037c..fbe34c4b68e15 100644 --- a/packages/kbn-grid-layout/grid/grid_panel.tsx +++ b/packages/kbn-grid-layout/grid/grid_panel.tsx @@ -7,63 +7,38 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import React, { forwardRef } from 'react'; + import { EuiIcon, EuiPanel, euiFullHeight, transparentize, useEuiOverflowScroll, + useEuiTheme, } from '@elastic/eui'; import { css } from '@emotion/react'; import { euiThemeVars } from '@kbn/ui-theme'; -import React, { useCallback, useRef } from 'react'; + import { GridPanelData, PanelInteractionEvent } from './types'; -export const GridPanel = ({ - activePanelId, - panelData, - renderPanelContents, - setInteractionEvent, -}: { - panelData: GridPanelData; - activePanelId: string | undefined; - renderPanelContents: (panelId: string) => React.ReactNode; - setInteractionEvent: (interactionData?: Omit) => void; -}) => { - const panelRef = useRef(null); +export const GridPanel = forwardRef< + HTMLDivElement, + { + panelData: GridPanelData; + activePanelId: string | undefined; + renderPanelContents: (panelId: string) => React.ReactNode; + interactionStart: ( + type: PanelInteractionEvent['type'], + e: React.MouseEvent + ) => void; + } +>(({ activePanelId, panelData, renderPanelContents, interactionStart }, panelRef) => { + const { euiTheme } = useEuiTheme(); const thisPanelActive = activePanelId === panelData.id; - const interactionStart = useCallback( - (type: 'drag' | 'resize', e: React.MouseEvent) => { - if (!panelRef.current) return; - e.preventDefault(); - e.stopPropagation(); - const panelRect = panelRef.current.getBoundingClientRect(); - setInteractionEvent({ - type, - id: panelData.id, - panelDiv: panelRef.current, - mouseOffsets: { - top: e.clientY - panelRect.top, - left: e.clientX - panelRect.left, - right: e.clientX - panelRect.right, - bottom: e.clientY - panelRect.bottom, - }, - }); - }, - [panelData.id, setInteractionEvent] - ); - return ( -
+
interactionStart('drag', e)} + onMouseUp={(e) => interactionStart('drop', e)} >
@@ -109,6 +93,7 @@ export const GridPanel = ({
interactionStart('resize', e)} + onMouseUp={(e) => interactionStart('drop', e)} css={css` right: 0; bottom: 0; @@ -139,4 +124,4 @@ export const GridPanel = ({
); -}; +}); diff --git a/packages/kbn-grid-layout/grid/grid_row.tsx b/packages/kbn-grid-layout/grid/grid_row.tsx index b2a9375c390b1..917f661c91740 100644 --- a/packages/kbn-grid-layout/grid/grid_row.tsx +++ b/packages/kbn-grid-layout/grid/grid_row.tsx @@ -7,13 +7,21 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import React, { forwardRef, useMemo, useRef } from 'react'; + import { EuiButtonIcon, EuiFlexGroup, EuiSpacer, EuiTitle, transparentize } from '@elastic/eui'; import { css } from '@emotion/react'; import { i18n } from '@kbn/i18n'; import { euiThemeVars } from '@kbn/ui-theme'; -import React, { forwardRef, useMemo } from 'react'; +import { useStateFromPublishingSubject } from '@kbn/presentation-publishing'; + import { GridPanel } from './grid_panel'; -import { GridRowData, PanelInteractionEvent, RuntimeGridSettings } from './types'; +import { + GridLayoutStateManager, + GridRowData, + PanelInteractionEvent, + RuntimeGridSettings, +} from './types'; const gridColor = transparentize(euiThemeVars.euiColorSuccess, 0.2); const getGridBackgroundCSS = (settings: RuntimeGridSettings) => { @@ -32,28 +40,31 @@ export const GridRow = forwardRef< rowIndex: number; rowData: GridRowData; toggleIsCollapsed: () => void; - activePanelId: string | undefined; targetRowIndex: number | undefined; runtimeSettings: RuntimeGridSettings; renderPanelContents: (panelId: string) => React.ReactNode; setInteractionEvent: (interactionData?: PanelInteractionEvent) => void; + gridLayoutStateManager: GridLayoutStateManager; } >( ( { rowData, rowIndex, - activePanelId, targetRowIndex, runtimeSettings, toggleIsCollapsed, renderPanelContents, setInteractionEvent, + gridLayoutStateManager, }, gridRef ) => { + const dragPreviewRef = useRef(null); + const activePanel = useStateFromPublishingSubject(gridLayoutStateManager.activePanel$); + const { gutterSize, columnCount, rowHeight } = runtimeSettings; - const isGridTargeted = activePanelId && targetRowIndex === rowIndex; + const isGridTargeted = activePanel?.id && targetRowIndex === rowIndex; // calculate row count based on the number of rows needed to fit all panels const rowCount = useMemo(() => { @@ -107,20 +118,58 @@ export const GridRow = forwardRef< { - if (partialInteractionEvent) { - setInteractionEvent({ - ...partialInteractionEvent, - targetRowIndex: rowIndex, - }); - return; + interactionStart={(type, e) => { + e.preventDefault(); + e.stopPropagation(); + const panelRef = gridLayoutStateManager.panelRefs.current[rowIndex][panelData.id]; + if (!panelRef) return; + + const panelRect = panelRef.getBoundingClientRect(); + setInteractionEvent({ + type, + id: panelData.id, + panelDiv: panelRef, + targetRowIndex: rowIndex, + mouseOffsets: { + top: e.clientY - panelRect.top, + left: e.clientX - panelRect.left, + right: e.clientX - panelRect.right, + bottom: e.clientY - panelRect.bottom, + }, + }); + }} + ref={(element) => { + if (!gridLayoutStateManager.panelRefs.current[rowIndex]) { + gridLayoutStateManager.panelRefs.current[rowIndex] = {}; } - setInteractionEvent(); + gridLayoutStateManager.panelRefs.current[rowIndex][panelData.id] = element; }} /> ))} + + {/* render the drag preview if this row is currently being targetted */} + {isGridTargeted && ( +
+ )}
)} diff --git a/packages/kbn-grid-layout/grid/types.ts b/packages/kbn-grid-layout/grid/types.ts index 3d63c9f6ea01a..3a88eeb33baba 100644 --- a/packages/kbn-grid-layout/grid/types.ts +++ b/packages/kbn-grid-layout/grid/types.ts @@ -46,21 +46,25 @@ export interface GridSettings { */ export type RuntimeGridSettings = GridSettings & { columnPixelWidth: number }; -export interface GridLayoutStateManager { - hideDragPreview: () => void; - updatePreviewElement: (rect: { +export interface ActivePanel { + id: string; + position: { top: number; left: number; bottom: number; right: number; - }) => void; + }; +} +export interface GridLayoutStateManager { gridDimensions$: BehaviorSubject; gridLayout$: BehaviorSubject; runtimeSettings$: BehaviorSubject; - rowRefs: React.MutableRefObject>; - dragPreviewRef: React.MutableRefObject; + activePanel$: BehaviorSubject; interactionEvent$: BehaviorSubject; + + rowRefs: React.MutableRefObject>; + panelRefs: React.MutableRefObject>; } /** @@ -70,7 +74,7 @@ export interface PanelInteractionEvent { /** * The type of interaction being performed. */ - type: 'drag' | 'resize'; + type: 'drag' | 'resize' | 'drop'; /** * The id of the panel being interacted with. diff --git a/packages/kbn-grid-layout/grid/use_grid_layout_events.ts b/packages/kbn-grid-layout/grid/use_grid_layout_events.ts index 34fa84eeb131e..dfbd013d3642a 100644 --- a/packages/kbn-grid-layout/grid/use_grid_layout_events.ts +++ b/packages/kbn-grid-layout/grid/use_grid_layout_events.ts @@ -8,8 +8,9 @@ */ import { useEffect, useRef } from 'react'; + import { resolveGridRow } from './resolve_grid_row'; -import { GridPanelData, GridLayoutStateManager } from './types'; +import { GridLayoutStateManager, GridPanelData } from './types'; export const isGridDataEqual = (a?: GridPanelData, b?: GridPanelData) => { return ( @@ -35,12 +36,11 @@ export const useGridLayoutEvents = ({ useEffect(() => { const { runtimeSettings$, interactionEvent$, gridLayout$ } = gridLayoutStateManager; const calculateUserEvent = (e: Event) => { - if (!interactionEvent$.value) return; + if (!interactionEvent$.value || interactionEvent$.value.type === 'drop') return; e.preventDefault(); e.stopPropagation(); const gridRowElements = gridLayoutStateManager.rowRefs.current; - const previewElement = gridLayoutStateManager.dragPreviewRef.current; const interactionEvent = interactionEvent$.value; const isResize = interactionEvent?.type === 'resize'; @@ -53,7 +53,7 @@ export const useGridLayoutEvents = ({ } })(); - if (!runtimeSettings$.value || !previewElement || !gridRowElements || !currentGridData) { + if (!runtimeSettings$.value || !gridRowElements || !currentGridData) { return; } @@ -68,7 +68,7 @@ export const useGridLayoutEvents = ({ bottom: mouseTargetPixel.y - interactionEvent.mouseOffsets.bottom, right: mouseTargetPixel.x - interactionEvent.mouseOffsets.right, }; - gridLayoutStateManager.updatePreviewElement(previewRect); + gridLayoutStateManager.activePanel$.next({ id: interactionEvent.id, position: previewRect }); // find the grid that the preview rect is over const previewBottom = @@ -154,29 +154,19 @@ export const useGridLayoutEvents = ({ const resolvedOriginGrid = resolveGridRow(originGrid); nextLayout[lastRowIndex] = resolvedOriginGrid; } + gridLayout$.next(nextLayout); } }; - const onMouseUp = (e: MouseEvent) => { - if (!interactionEvent$.value) return; - e.preventDefault(); - e.stopPropagation(); - - interactionEvent$.next(undefined); - gridLayoutStateManager.hideDragPreview(); - }; - const onMouseMove = (e: MouseEvent) => { mouseClientPosition.current = { x: e.clientX, y: e.clientY }; calculateUserEvent(e); }; - document.addEventListener('mouseup', onMouseUp); document.addEventListener('mousemove', onMouseMove); document.addEventListener('scroll', calculateUserEvent); return () => { - document.removeEventListener('mouseup', onMouseUp); document.removeEventListener('mousemove', onMouseMove); document.removeEventListener('scroll', calculateUserEvent); }; diff --git a/packages/kbn-grid-layout/grid/use_grid_layout_state.ts b/packages/kbn-grid-layout/grid/use_grid_layout_state.ts index e10ce12fe5b6b..cdb99a9ebbfd0 100644 --- a/packages/kbn-grid-layout/grid/use_grid_layout_state.ts +++ b/packages/kbn-grid-layout/grid/use_grid_layout_state.ts @@ -7,10 +7,12 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import { i18n } from '@kbn/i18n'; import { useEffect, useMemo, useRef } from 'react'; -import { BehaviorSubject, debounceTime } from 'rxjs'; +import { BehaviorSubject, combineLatest, debounceTime, map, retry } from 'rxjs'; import useResizeObserver, { type ObservedSize } from 'use-resize-observer/polyfilled'; import { + ActivePanel, GridLayoutData, GridLayoutStateManager, GridSettings, @@ -27,7 +29,7 @@ export const useGridLayoutState = ({ setDimensionsRef: (instance: HTMLDivElement | null) => void; } => { const rowRefs = useRef>([]); - const dragPreviewRef = useRef(null); + const panelRefs = useRef>([]); // eslint-disable-next-line react-hooks/exhaustive-deps const { initialLayout, gridSettings } = useMemo(() => getCreationOptions(), []); @@ -36,6 +38,7 @@ export const useGridLayoutState = ({ const gridLayout$ = new BehaviorSubject(initialLayout); const gridDimensions$ = new BehaviorSubject({ width: 0, height: 0 }); const interactionEvent$ = new BehaviorSubject(undefined); + const activePanel$ = new BehaviorSubject(undefined); const runtimeSettings$ = new BehaviorSubject({ ...gridSettings, columnPixelWidth: 0, @@ -43,41 +46,21 @@ export const useGridLayoutState = ({ return { rowRefs, + panelRefs, gridLayout$, - dragPreviewRef, + activePanel$, gridDimensions$, runtimeSettings$, interactionEvent$, - updatePreviewElement: (previewRect: { - top: number; - bottom: number; - left: number; - right: number; - }) => { - if (!dragPreviewRef.current) return; - dragPreviewRef.current.style.opacity = '1'; - dragPreviewRef.current.style.left = `${previewRect.left}px`; - dragPreviewRef.current.style.top = `${previewRect.top}px`; - dragPreviewRef.current.style.width = `${Math.max( - previewRect.right - previewRect.left, - runtimeSettings$.value.columnPixelWidth - )}px`; - dragPreviewRef.current.style.height = `${Math.max( - previewRect.bottom - previewRect.top, - runtimeSettings$.value.rowHeight - )}px`; - }, - hideDragPreview: () => { - if (!dragPreviewRef.current) return; - dragPreviewRef.current.style.opacity = '0'; - }, }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { - // debounce width changes to avoid unnecessary column width recalculation. - const subscription = gridLayoutStateManager.gridDimensions$ + /** + * debounce width changes to avoid unnecessary column width recalculation. + */ + const resizeSubscription = gridLayoutStateManager.gridDimensions$ .pipe(debounceTime(250)) .subscribe((dimensions) => { const elementWidth = dimensions.width ?? 0; @@ -86,7 +69,116 @@ export const useGridLayoutState = ({ gridSettings.columnCount; gridLayoutStateManager.runtimeSettings$.next({ ...gridSettings, columnPixelWidth }); }); - return () => subscription.unsubscribe(); + + /** + * on layout change, update the styles of every panel so that it renders as expected + */ + const onLayoutChangeSubscription = combineLatest([ + gridLayoutStateManager.gridLayout$, + gridLayoutStateManager.activePanel$, + ]) + .pipe( + map(([gridLayout, activePanel]) => { + // wait for all panel refs to be ready before continuing + for (let rowIndex = 0; rowIndex < gridLayout.length; rowIndex++) { + const currentRow = gridLayout[rowIndex]; + Object.keys(currentRow.panels).forEach((key) => { + const panelRef = gridLayoutStateManager.panelRefs.current[rowIndex][key]; + if (!panelRef && !currentRow.isCollapsed) { + throw new Error( + i18n.translate('kbnGridLayout.panelRefNotFoundError', { + defaultMessage: 'Panel reference does not exist', // the retry will catch this error + }) + ); + } + }); + } + return { gridLayout, activePanel }; + }), + retry({ delay: 10 }) // retry until panel references all exist + ) + .subscribe(({ gridLayout, activePanel }) => { + const runtimeSettings = gridLayoutStateManager.runtimeSettings$.getValue(); + const currentInteractionEvent = gridLayoutStateManager.interactionEvent$.getValue(); + + for (let rowIndex = 0; rowIndex < gridLayout.length; rowIndex++) { + if (activePanel && rowIndex !== currentInteractionEvent?.targetRowIndex) { + /** + * If there is an interaction event happening but the current row is not being targetted, it + * does not need to be re-rendered; so, skip setting the panel styles of this row. + * + * If there is **no** interaction event, then this is the initial render so the styles of every + * panel should be initialized; so, don't skip setting the panel styles. + */ + continue; + } + + // re-render the targetted row + const currentRow = gridLayout[rowIndex]; + Object.keys(currentRow.panels).forEach((key) => { + const panel = currentRow.panels[key]; + const panelRef = gridLayoutStateManager.panelRefs.current[rowIndex][key]; + if (!panelRef) { + return; + } + + const isResize = currentInteractionEvent?.type === 'resize'; + if (panel.id === activePanel?.id) { + // if the current panel is active, give it fixed positioning depending on the interaction event + const { position: draggingPosition } = activePanel; + + if (isResize) { + // if the current panel is being resized, ensure it is not shrunk past the size of a single cell + panelRef.style.width = `${Math.max( + draggingPosition.right - draggingPosition.left, + runtimeSettings.columnPixelWidth + )}px`; + panelRef.style.height = `${Math.max( + draggingPosition.bottom - draggingPosition.top, + runtimeSettings.rowHeight + )}px`; + + // undo any "lock to grid" styles **except** for the top left corner, which stays locked + panelRef.style.gridColumnStart = `${panel.column + 1}`; + panelRef.style.gridRowStart = `${panel.row + 1}`; + panelRef.style.gridColumnEnd = ``; + panelRef.style.gridRowEnd = ``; + } else { + // if the current panel is being dragged, render it with a fixed position + size + panelRef.style.position = 'fixed'; + panelRef.style.left = `${draggingPosition.left}px`; + panelRef.style.top = `${draggingPosition.top}px`; + panelRef.style.width = `${draggingPosition.right - draggingPosition.left}px`; + panelRef.style.height = `${draggingPosition.bottom - draggingPosition.top}px`; + + // undo any "lock to grid" styles + panelRef.style.gridColumnStart = ``; + panelRef.style.gridRowStart = ``; + panelRef.style.gridColumnEnd = ``; + panelRef.style.gridRowEnd = ``; + } + } else { + // if the panel is not being dragged and/or resized, undo any fixed position styles + panelRef.style.position = ''; + panelRef.style.left = ``; + panelRef.style.top = ``; + panelRef.style.width = ``; + panelRef.style.height = ``; + + // and render the panel locked to the grid + panelRef.style.gridColumnStart = `${panel.column + 1}`; + panelRef.style.gridColumnEnd = `${panel.column + 1 + panel.width}`; + panelRef.style.gridRowStart = `${panel.row + 1}`; + panelRef.style.gridRowEnd = `${panel.row + 1 + panel.height}`; + } + }); + } + }); + + return () => { + resizeSubscription.unsubscribe(); + onLayoutChangeSubscription.unsubscribe(); + }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts b/packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts index 441df3948632b..30682d763e0b0 100644 --- a/packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts +++ b/packages/kbn-kibana-manifest-schema/src/kibana_json_v2_schema.ts @@ -48,6 +48,20 @@ export const MANIFEST_V2: JSONSchema = { For additional codeowners, the value can be an array of user/team names. `, }, + group: { + enum: ['common', 'platform', 'observability', 'security', 'search'], + description: desc` + Specifies the group to which this module pertains. + `, + default: 'common', + }, + visibility: { + enum: ['private', 'shared'], + description: desc` + Specifies the visibility of this module, i.e. whether it can be accessed by everybody or only modules in the same group + `, + default: 'shared', + }, devOnly: { type: 'boolean', description: desc` diff --git a/packages/kbn-language-documentation/src/sections/generated/scalar_functions.tsx b/packages/kbn-language-documentation/src/sections/generated/scalar_functions.tsx index 3b74c73cd2a9a..32e66931cedcd 100644 --- a/packages/kbn-language-documentation/src/sections/generated/scalar_functions.tsx +++ b/packages/kbn-language-documentation/src/sections/generated/scalar_functions.tsx @@ -1139,6 +1139,42 @@ export const functions = { | EVAL message = CONCAT("'", message, "'") | EVAL color = CONCAT("'", color, "'") \`\`\` + `, + description: + 'Text is in markdown. Do not translate function names, special characters, or field names like sum(bytes)', + ignoreTag: true, + } + )} + /> + ), + }, + // Do not edit manually... automatically generated by scripts/generate_esql_docs.ts + { + label: i18n.translate('languageDocumentation.documentationESQL.match', { + defaultMessage: 'MATCH', + }), + description: ( + + + ### MATCH + Performs a match query on the specified field. Returns true if the provided query matches the row. + + \`\`\` + from books + | where match(author, "Faulkner") + | keep book_no, author + | sort book_no + | limit 5; + \`\`\` `, description: 'Text is in markdown. Do not translate function names, special characters, or field names like sum(bytes)', @@ -1801,6 +1837,39 @@ export const functions = { | EVAL result = POW(base, exponent) \`\`\` Note: It is still possible to overflow a double result here; in that case, null will be returned. + `, + description: + 'Text is in markdown. Do not translate function names, special characters, or field names like sum(bytes)', + ignoreTag: true, + })} + /> + ), + }, + // Do not edit manually... automatically generated by scripts/generate_esql_docs.ts + { + label: i18n.translate('languageDocumentation.documentationESQL.qstr', { + defaultMessage: 'QSTR', + }), + description: ( + + + ### QSTR + Performs a query string query. Returns true if the provided query string matches the row. + + \`\`\` + from books + | where qstr("author: Faulkner") + | keep book_no, author + | sort book_no + | limit 5; + \`\`\` `, description: 'Text is in markdown. Do not translate function names, special characters, or field names like sum(bytes)', diff --git a/packages/kbn-lens-embeddable-utils/config_builder/charts/xy.test.ts b/packages/kbn-lens-embeddable-utils/config_builder/charts/xy.test.ts index 0e197b2bfb98b..9b9e9aa96575e 100644 --- a/packages/kbn-lens-embeddable-utils/config_builder/charts/xy.test.ts +++ b/packages/kbn-lens-embeddable-utils/config_builder/charts/xy.test.ts @@ -143,8 +143,8 @@ test('generates xy chart config', async () => { "yLeft": true, "yRight": true, }, - "emphasizeFitting": false, - "fittingFunction": "None", + "emphasizeFitting": true, + "fittingFunction": "Linear", "gridlinesVisibilitySettings": Object { "x": true, "yLeft": true, diff --git a/packages/kbn-lens-embeddable-utils/config_builder/charts/xy.ts b/packages/kbn-lens-embeddable-utils/config_builder/charts/xy.ts index b18a1caec2ffa..ccf68e9905621 100644 --- a/packages/kbn-lens-embeddable-utils/config_builder/charts/xy.ts +++ b/packages/kbn-lens-embeddable-utils/config_builder/charts/xy.ts @@ -51,8 +51,8 @@ function buildVisualizationState(config: LensXYConfig): XYState { hideEndzones: true, preferredSeriesType: 'line', valueLabels: 'hide', - emphasizeFitting: config?.emphasizeFitting ?? false, - fittingFunction: config?.fittingFunction ?? 'None', + emphasizeFitting: config?.emphasizeFitting ?? true, + fittingFunction: config?.fittingFunction ?? 'Linear', yLeftExtent: { mode: config.yBounds?.mode ?? 'full', lowerBound: config.yBounds?.lowerBound, diff --git a/packages/kbn-manifest/README.md b/packages/kbn-manifest/README.md new file mode 100644 index 0000000000000..a7dc2054252dc --- /dev/null +++ b/packages/kbn-manifest/README.md @@ -0,0 +1,30 @@ +# @kbn/manifest + +This package contains a CLI to list `kibana.jsonc` manifests and also to mass update their properties. + +## Usage + +To list all `kibana.jsonc` manifests, run the following command from the root of the Kibana repo: + +```sh +node scripts/manifest --list all +``` + +To print a manifest by packageId or by pluginId, run the following command from the root of the Kibana repo: + +```sh +node scripts/manifest --package @kbn/package_name +node scripts/manifest --plugin pluginId +``` + +To update properties in one or more manifest files, run the following command from the root of the Kibana repo: + +```sh +node scripts/manifest \ +--package @kbn/package_1 \ +--package @kbn/package_2 \ +# ... +--package @kbn/package_N \ +--set path.to.property1=value \ +--set property2=value +``` diff --git a/packages/kbn-manifest/index.ts b/packages/kbn-manifest/index.ts new file mode 100644 index 0000000000000..5fc4727a1a72d --- /dev/null +++ b/packages/kbn-manifest/index.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { run } from '@kbn/dev-cli-runner'; +import { listManifestFiles, printManifest, updateManifest } from './manifest'; + +/** + * A CLI to manipulate Kibana package manifest files + */ +export const runKbnManifestCli = () => { + run( + async ({ log, flags }) => { + if (flags.list === 'all') { + listManifestFiles(flags, log); + } else { + if (!flags.package && !flags.plugin) { + throw new Error('You must specify the identifer of the --package or --plugin to update.'); + } + await updateManifest(flags, log); + await printManifest(flags, log); + } + }, + { + log: { + defaultLevel: 'info', + }, + flags: { + string: ['list', 'package', 'plugin', 'set', 'unset'], + help: ` + Usage: node scripts/manifest --package --set group=platform --set visibility=private + --list all List all the manifests + --package [packageId] Select a package to update. + --plugin [pluginId] Select a plugin to update. + --set [property] [value] Set the desired "[property]": "[value]" + --unset [property] Removes the desired "[property]: value" from the manifest + `, + }, + } + ); +}; diff --git a/packages/kbn-manifest/jest.config.js b/packages/kbn-manifest/jest.config.js new file mode 100644 index 0000000000000..ed8288d9fb712 --- /dev/null +++ b/packages/kbn-manifest/jest.config.js @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../..', + roots: ['/packages/kbn-manifest'], +}; diff --git a/packages/kbn-manifest/kibana.jsonc b/packages/kbn-manifest/kibana.jsonc new file mode 100644 index 0000000000000..27f2d95e65501 --- /dev/null +++ b/packages/kbn-manifest/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-server", + "id": "@kbn/manifest", + "owner": "@elastic/kibana-core" +} diff --git a/packages/kbn-manifest/manifest.ts b/packages/kbn-manifest/manifest.ts new file mode 100644 index 0000000000000..a839dba7b4077 --- /dev/null +++ b/packages/kbn-manifest/manifest.ts @@ -0,0 +1,113 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { join } from 'path'; +import { writeFile } from 'fs/promises'; +import { flatMap, unset } from 'lodash'; +import { set } from '@kbn/safer-lodash-set'; +import type { ToolingLog } from '@kbn/tooling-log'; +import type { Flags } from '@kbn/dev-cli-runner'; +import { type Package, getPackages } from '@kbn/repo-packages'; +import { REPO_ROOT } from '@kbn/repo-info'; + +const MANIFEST_FILE = 'kibana.jsonc'; + +const getKibanaJsonc = (flags: Flags, log: ToolingLog): Package[] => { + const modules = getPackages(REPO_ROOT); + + let packageIds: string[] = []; + let pluginIds: string[] = []; + + if (typeof flags.package === 'string') { + packageIds = [flags.package].filter(Boolean); + } else if (Array.isArray(flags.package)) { + packageIds = [...flags.package].filter(Boolean); + } + + if (typeof flags.plugin === 'string') { + pluginIds = [flags.plugin].filter(Boolean); + } else if (Array.isArray(flags.plugin)) { + pluginIds = [...flags.plugin].filter(Boolean); + } + + return modules.filter( + (pkg) => + packageIds.includes(pkg.id) || (pkg.isPlugin() && pluginIds.includes(pkg.manifest.plugin.id)) + ); +}; + +export const listManifestFiles = (flags: Flags, log: ToolingLog) => { + const modules = getPackages(REPO_ROOT); + modules + .filter((module) => module.manifest.type === 'plugin') + .forEach((module) => { + log.info(join(module.directory, MANIFEST_FILE), module.id); + }); +}; + +export const printManifest = (flags: Flags, log: ToolingLog) => { + const kibanaJsoncs = getKibanaJsonc(flags, log); + kibanaJsoncs.forEach((kibanaJsonc) => { + const manifestPath = join(kibanaJsonc.directory, MANIFEST_FILE); + log.info('\n\nShowing manifest: ', manifestPath); + log.info(JSON.stringify(kibanaJsonc, null, 2)); + }); +}; + +export const updateManifest = async (flags: Flags, log: ToolingLog) => { + let toSet: string[] = []; + let toUnset: string[] = []; + + if (typeof flags.set === 'string') { + toSet = [flags.set].filter(Boolean); + } else if (Array.isArray(flags.set)) { + toSet = [...flags.set].filter(Boolean); + } + + if (typeof flags.unset === 'string') { + toUnset = [flags.unset].filter(Boolean); + } else if (Array.isArray(flags.unset)) { + toUnset = [...flags.unset].filter(Boolean); + } + + if (!toSet.length && !toUnset.length) { + // no need to update anything + return; + } + + const kibanaJsoncs = getKibanaJsonc(flags, log); + + for (let i = 0; i < kibanaJsoncs.length; ++i) { + const kibanaJsonc = kibanaJsoncs[i]; + + if (kibanaJsonc?.manifest) { + const manifestPath = join(kibanaJsonc.directory, MANIFEST_FILE); + log.info('Updating manifest: ', manifestPath); + toSet.forEach((propValue) => { + const [prop, value] = propValue.split('='); + log.info(`Setting "${prop}": "${value}"`); + set(kibanaJsonc.manifest, prop, value); + }); + + toUnset.forEach((prop) => { + log.info(`Removing "${prop}"`); + unset(kibanaJsonc.manifest, prop); + }); + + sanitiseManifest(kibanaJsonc); + + await writeFile(manifestPath, JSON.stringify(kibanaJsonc.manifest, null, 2)); + log.info('DONE'); + } + } +}; + +const sanitiseManifest = (kibanaJsonc: Package) => { + kibanaJsonc.manifest.owner = flatMap(kibanaJsonc.manifest.owner.map((owner) => owner.split(' '))); +}; diff --git a/packages/kbn-manifest/package.json b/packages/kbn-manifest/package.json new file mode 100644 index 0000000000000..52304cc4c1e21 --- /dev/null +++ b/packages/kbn-manifest/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/manifest", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0" +} diff --git a/packages/kbn-manifest/tsconfig.json b/packages/kbn-manifest/tsconfig.json new file mode 100644 index 0000000000000..1ee41aafca1ee --- /dev/null +++ b/packages/kbn-manifest/tsconfig.json @@ -0,0 +1,23 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/dev-cli-runner", + "@kbn/repo-info", + "@kbn/repo-packages", + "@kbn/safer-lodash-set", + "@kbn/tooling-log", + ] +} diff --git a/packages/kbn-monaco/scripts/utils/create_autocomplete_definitions.js b/packages/kbn-monaco/scripts/utils/create_autocomplete_definitions.js index d6eeac2e20f61..3750b8697f493 100644 --- a/packages/kbn-monaco/scripts/utils/create_autocomplete_definitions.js +++ b/packages/kbn-monaco/scripts/utils/create_autocomplete_definitions.js @@ -36,7 +36,7 @@ const getDisplayName = (name, imported) => { displayName = name.split('.').pop() || name; } - return displayName.replace('$', '.'); + return displayName.replace(/^\$/g, '.'); }; /** diff --git a/packages/kbn-monaco/src/esql/lib/esql_theme.ts b/packages/kbn-monaco/src/esql/lib/esql_theme.ts index f98eddefd8eab..bf5e2c597eb6c 100644 --- a/packages/kbn-monaco/src/esql/lib/esql_theme.ts +++ b/packages/kbn-monaco/src/esql/lib/esql_theme.ts @@ -47,7 +47,6 @@ export const buildESQlTheme = (): monaco.editor.IStandaloneThemeData => ({ [ 'dev_metrics', 'metadata', - 'dev_match', 'mv_expand', 'stats', 'dev_inlinestats', @@ -75,6 +74,7 @@ export const buildESQlTheme = (): monaco.editor.IStandaloneThemeData => ({ 'asc', 'desc', 'nulls_order', + 'match', ], euiThemeVars.euiColorAccentText, true // isBold diff --git a/packages/kbn-monaco/src/painless/worker/painless_worker.ts b/packages/kbn-monaco/src/painless/worker/painless_worker.ts index 332199f45b8ee..80489ccdbc589 100644 --- a/packages/kbn-monaco/src/painless/worker/painless_worker.ts +++ b/packages/kbn-monaco/src/painless/worker/painless_worker.ts @@ -44,7 +44,7 @@ export class PainlessWorker implements BaseWorkerDefinition { fields?: PainlessAutocompleteField[] ): PainlessCompletionResult { // Array of the active line words, e.g., [boolean, isTrue, =, true] - const words = currentLineChars.replace('\t', '').split(' '); + const words = currentLineChars.replace(/\t/g, '').split(/\s/); const autocompleteSuggestions: PainlessCompletionResult = getAutocompleteSuggestions( context, diff --git a/packages/kbn-repo-info/types.ts b/packages/kbn-repo-info/types.ts index a4776c28760a2..338881e878fdc 100644 --- a/packages/kbn-repo-info/types.ts +++ b/packages/kbn-repo-info/types.ts @@ -7,6 +7,9 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +export type ModuleGroup = 'platform' | 'observability' | 'search' | 'security' | 'common'; +export type ModuleVisibility = 'private' | 'shared'; + export interface KibanaPackageJson { name: string; version: string; @@ -27,4 +30,6 @@ export interface KibanaPackageJson { [name: string]: string | undefined; }; [key: string]: unknown; + group?: ModuleGroup; + visibility?: ModuleVisibility; } diff --git a/packages/kbn-repo-packages/modern/package.js b/packages/kbn-repo-packages/modern/package.js index 1c44cd0cf86d9..3ec33a69e841a 100644 --- a/packages/kbn-repo-packages/modern/package.js +++ b/packages/kbn-repo-packages/modern/package.js @@ -116,6 +116,22 @@ class Package { * @readonly */ this.id = manifest.id; + + const { group, visibility } = this.determineGroupAndVisibility(); + + /** + * the group to which this package belongs + * @type {import('@kbn/repo-info/types').ModuleGroup} + * @readonly + */ + + this.group = group; + /** + * the visibility of this package, i.e. whether it can be accessed by everybody or only modules in the same group + * @type {import('@kbn/repo-info/types').ModuleVisibility} + * @readonly + */ + this.visibility = visibility; } /** @@ -140,6 +156,24 @@ class Package { return this.manifest.type === 'plugin'; } + /** + * Returns the group to which this package belongs + * @readonly + * @returns {import('@kbn/repo-info/types').ModuleGroup} + */ + getGroup() { + return this.group; + } + + /** + * Returns the package visibility, i.e. whether it can be accessed by everybody or only packages in the same group + * @readonly + * @returns {import('@kbn/repo-info/types').ModuleVisibility} + */ + getVisibility() { + return this.visibility; + } + /** * Returns true if the package represents some type of plugin * @returns {import('./types').PluginCategoryInfo} @@ -158,6 +192,7 @@ class Package { const oss = !dir.startsWith('x-pack/'); const example = dir.startsWith('examples/') || dir.startsWith('x-pack/examples/'); const testPlugin = dir.startsWith('test/') || dir.startsWith('x-pack/test/'); + return { oss, example, @@ -165,6 +200,40 @@ class Package { }; } + determineGroupAndVisibility() { + const dir = this.normalizedRepoRelativeDir; + + /** @type {import('@kbn/repo-info/types').ModuleGroup} */ + let group = 'common'; + /** @type {import('@kbn/repo-info/types').ModuleVisibility} */ + let visibility = 'shared'; + + if (dir.startsWith('src/platform/') || dir.startsWith('x-pack/platform/')) { + group = 'platform'; + visibility = + /src\/platform\/[^\/]+\/shared/.test(dir) || /x-pack\/platform\/[^\/]+\/shared/.test(dir) + ? 'shared' + : 'private'; + } else if (dir.startsWith('x-pack/solutions/search/')) { + group = 'search'; + visibility = 'private'; + } else if (dir.startsWith('x-pack/solutions/security/')) { + group = 'security'; + visibility = 'private'; + } else if (dir.startsWith('x-pack/solutions/observability/')) { + group = 'observability'; + visibility = 'private'; + } else { + group = this.manifest.group ?? 'common'; + // if the group is 'private-only', enforce it + visibility = ['search', 'security', 'observability'].includes(group) + ? 'private' + : this.manifest.visibility ?? 'shared'; + } + + return { group, visibility }; + } + /** * Custom inspect handler so that logging variables in scripts/generate doesn't * print all the BUILD.bazel files diff --git a/packages/kbn-repo-packages/modern/parse_package_manifest.js b/packages/kbn-repo-packages/modern/parse_package_manifest.js index 40a6f7bf1059b..46004983848bb 100644 --- a/packages/kbn-repo-packages/modern/parse_package_manifest.js +++ b/packages/kbn-repo-packages/modern/parse_package_manifest.js @@ -225,16 +225,20 @@ function validatePackageManifest(parsed, repoRoot, path) { type, id, owner, + group, + visibility, devOnly, - plugin, - sharedBrowserBundle, build, description, serviceFolders, ...extra - } = parsed; + } = /** @type {import('./types').PackageManifestBaseFields} */ (/** @type {unknown} */ (parsed)); - const extraKeys = Object.keys(extra); + const { plugin, sharedBrowserBundle } = parsed; + + const extraKeys = Object.keys(extra).filter( + (key) => !['plugin', 'sharedBrowserBundle'].includes(key) + ); if (extraKeys.length) { throw new Error(`unexpected keys in package manifest [${extraKeys.join(', ')}]`); } @@ -258,6 +262,25 @@ function validatePackageManifest(parsed, repoRoot, path) { ); } + if ( + group !== undefined && + (!isSomeString(group) || + !['platform', 'search', 'security', 'observability', 'common'].includes(group)) + ) { + throw err( + `plugin.group`, + group, + `must have a valid value ("platform" | "search" | "security" | "observability" | "common")` + ); + } + + if ( + visibility !== undefined && + (!isSomeString(visibility) || !['private', 'shared'].includes(visibility)) + ) { + throw err(`plugin.visibility`, visibility, `must have a valid value ("private" | "shared")`); + } + if (devOnly !== undefined && typeof devOnly !== 'boolean') { throw err(`devOnly`, devOnly, `must be a boolean when defined`); } @@ -273,6 +296,8 @@ function validatePackageManifest(parsed, repoRoot, path) { const base = { id, owner: Array.isArray(owner) ? owner : [owner], + group, + visibility, devOnly, build: validatePackageManifestBuild(build), description, diff --git a/packages/kbn-repo-packages/modern/types.ts b/packages/kbn-repo-packages/modern/types.ts index 41250de7c6346..c883e33d82497 100644 --- a/packages/kbn-repo-packages/modern/types.ts +++ b/packages/kbn-repo-packages/modern/types.ts @@ -7,6 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import type { ModuleGroup, ModuleVisibility } from '@kbn/repo-info/types'; import type { Package } from './package'; import type { PLUGIN_CATEGORY } from './plugin_category_info'; @@ -44,7 +45,7 @@ export type KibanaPackageType = | 'functional-tests' | 'test-helper'; -interface PackageManifestBaseFields { +export interface PackageManifestBaseFields { /** * The type of this package. Package types define how a package can and should * be used/built. Some package types also change the way that packages are @@ -91,6 +92,14 @@ interface PackageManifestBaseFields { * @deprecated */ serviceFolders?: string[]; + /** + * Specifies the group to which this package belongs + */ + group?: ModuleGroup; + /** + * Specifies the package visibility, i.e. whether it can be accessed by everybody or only packages in the same group + */ + visibility?: ModuleVisibility; } export interface PluginPackageManifest extends PackageManifestBaseFields { diff --git a/packages/kbn-repo-packages/tsconfig.json b/packages/kbn-repo-packages/tsconfig.json index 19c7e8d59f651..be62cc1a4c90b 100644 --- a/packages/kbn-repo-packages/tsconfig.json +++ b/packages/kbn-repo-packages/tsconfig.json @@ -14,5 +14,8 @@ ], "exclude": [ "target/**/*", + ], + "kbn_references": [ + "@kbn/repo-info", ] } diff --git a/packages/kbn-repo-source-classifier/src/group.ts b/packages/kbn-repo-source-classifier/src/group.ts new file mode 100644 index 0000000000000..8103d5c82c590 --- /dev/null +++ b/packages/kbn-repo-source-classifier/src/group.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import type { ModuleGroup, ModuleVisibility } from '@kbn/repo-info/types'; + +interface ModuleAttrs { + group: ModuleGroup; + visibility: ModuleVisibility; +} + +const DEFAULT_MODULE_ATTRS: ModuleAttrs = { + group: 'common', + visibility: 'shared', +}; + +const MODULE_GROUPING_BY_PATH: Record = { + 'src/platform/plugins/shared': { + group: 'platform', + visibility: 'shared', + }, + 'src/platform/plugins/internal': { + group: 'platform', + visibility: 'private', + }, + 'x-pack/platform/plugins/shared': { + group: 'platform', + visibility: 'shared', + }, + 'x-pack/platform/plugins/internal': { + group: 'platform', + visibility: 'private', + }, + 'x-pack/solutions/observability/plugins': { + group: 'observability', + visibility: 'private', + }, + 'x-pack/solutions/security/plugins': { + group: 'security', + visibility: 'private', + }, + 'x-pack/solutions/search/plugins': { + group: 'search', + visibility: 'private', + }, +}; + +/** + * Determine a plugin's grouping information based on the path where it is defined + * @param packageRelativePath the path in the repo where the package is located + * @returns The grouping information that corresponds to the given path + */ +export function inferGroupAttrsFromPath(packageRelativePath: string): ModuleAttrs { + const grouping = Object.entries(MODULE_GROUPING_BY_PATH).find(([chunk]) => + packageRelativePath.startsWith(chunk) + )?.[1]; + return grouping ?? DEFAULT_MODULE_ATTRS; +} diff --git a/packages/kbn-repo-source-classifier/src/module_id.ts b/packages/kbn-repo-source-classifier/src/module_id.ts index 6af8ece2438fa..284ffe26de0db 100644 --- a/packages/kbn-repo-source-classifier/src/module_id.ts +++ b/packages/kbn-repo-source-classifier/src/module_id.ts @@ -7,16 +7,24 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { ModuleType } from './module_type'; -import { PkgInfo } from './pkg_info'; +import type { KibanaPackageManifest } from '@kbn/repo-packages'; +import type { ModuleGroup, ModuleVisibility } from '@kbn/repo-info/types'; +import type { ModuleType } from './module_type'; +import type { PkgInfo } from './pkg_info'; export interface ModuleId { /** Type of the module */ type: ModuleType; + /** Specifies the group to which this module belongs */ + group: ModuleGroup; + /** Specifies the module visibility, i.e. whether it can be accessed by everybody or only modules in the same group */ + visibility: ModuleVisibility; /** repo relative path to the module's source file */ repoRel: string; /** info about the package the source file is within, in the case the file is found within a package */ pkgInfo?: PkgInfo; + /** The type of package, as described in the manifest */ + manifest?: KibanaPackageManifest; /** path segments of the dirname of this */ dirs: string[]; } diff --git a/packages/kbn-repo-source-classifier/src/repo_source_classifier.ts b/packages/kbn-repo-source-classifier/src/repo_source_classifier.ts index 470dd3c424421..c0ab29f659ebd 100644 --- a/packages/kbn-repo-source-classifier/src/repo_source_classifier.ts +++ b/packages/kbn-repo-source-classifier/src/repo_source_classifier.ts @@ -7,11 +7,14 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { ImportResolver } from '@kbn/import-resolver'; -import { ModuleId } from './module_id'; -import { ModuleType } from './module_type'; +import type { ImportResolver } from '@kbn/import-resolver'; +import type { ModuleGroup, ModuleVisibility } from '@kbn/repo-info/types'; +import type { KibanaPackageManifest } from '@kbn/repo-packages/modern/types'; +import type { ModuleId } from './module_id'; +import type { ModuleType } from './module_type'; import { RANDOM_TEST_FILE_NAMES, TEST_DIR, TEST_TAG } from './config'; import { RepoPath } from './repo_path'; +import { inferGroupAttrsFromPath } from './group'; const STATIC_EXTS = new Set( 'json|woff|woff2|ttf|eot|svg|ico|png|jpg|gif|jpeg|html|md|txt|tmpl|xml' @@ -231,7 +234,43 @@ export class RepoSourceClassifier { return 'common package'; } - classify(absolute: string) { + private getManifest(path: RepoPath): KibanaPackageManifest | undefined { + const pkgInfo = path.getPkgInfo(); + return pkgInfo?.pkgId ? this.resolver.getPkgManifest(pkgInfo!.pkgId) : undefined; + } + /** + * Determine the "group" of a file + */ + private getGroup(path: RepoPath): ModuleGroup { + const attrs = inferGroupAttrsFromPath(path.getRepoRel()); + const manifest = this.getManifest(path); + + if (attrs.group !== 'common') { + // this package has been moved to a 'group-specific' folder, the group is determined by its location + return attrs.group; + } else { + // the package is still in its original location, allow Manifest to dictate its group + return manifest?.group ?? 'common'; + } + } + + /** + * Determine the "visibility" of a file + */ + private getVisibility(path: RepoPath): ModuleVisibility { + const attrs = inferGroupAttrsFromPath(path.getRepoRel()); + const manifest = this.getManifest(path); + + if (attrs.group !== 'common') { + // this package has been moved to a 'group-specific' folder, the visibility is determined by its location + return attrs.visibility; + } else { + // the package is still in its original location, allow Manifest to dictate its visibility + return manifest?.visibility ?? 'shared'; + } + } + + classify(absolute: string): ModuleId { const path = this.getRepoPath(absolute); const cached = this.ids.get(path); @@ -241,8 +280,12 @@ export class RepoSourceClassifier { const id: ModuleId = { type: this.getType(path), + group: this.getGroup(path), + visibility: this.getVisibility(path), repoRel: path.getRepoRel(), pkgInfo: path.getPkgInfo() ?? undefined, + manifest: + (path.getPkgInfo() && this.resolver.getPkgManifest(path.getPkgInfo()!.pkgId)) ?? undefined, dirs: path.getSegs(), }; this.ids.set(path, id); diff --git a/packages/kbn-repo-source-classifier/tsconfig.json b/packages/kbn-repo-source-classifier/tsconfig.json index f41dffcd32f06..418b114eebafa 100644 --- a/packages/kbn-repo-source-classifier/tsconfig.json +++ b/packages/kbn-repo-source-classifier/tsconfig.json @@ -13,6 +13,7 @@ "kbn_references": [ "@kbn/import-resolver", "@kbn/repo-info", + "@kbn/repo-packages", ], "exclude": [ "target/**/*", diff --git a/packages/kbn-reporting/common/errors.ts b/packages/kbn-reporting/common/errors.ts index 9f45a1b6ae1d5..45a299115bf1b 100644 --- a/packages/kbn-reporting/common/errors.ts +++ b/packages/kbn-reporting/common/errors.ts @@ -152,3 +152,10 @@ export class VisualReportingSoftDisabledError extends ReportingError { }); } } + +export class ReportingSavedObjectNotFoundError extends ReportingError { + static code = 'reporting_saved_object_not_found_error' as const; + public get code(): string { + return ReportingSavedObjectNotFoundError.code; + } +} diff --git a/packages/kbn-router-to-openapispec/openapi-types.d.ts b/packages/kbn-router-to-openapispec/openapi-types.d.ts index 90c034a855fdc..9689ed803a152 100644 --- a/packages/kbn-router-to-openapispec/openapi-types.d.ts +++ b/packages/kbn-router-to-openapispec/openapi-types.d.ts @@ -13,7 +13,7 @@ export * from 'openapi-types'; declare module 'openapi-types' { export namespace OpenAPIV3 { export interface BaseSchemaObject { - // Custom OpenAPI field added by Kibana for a new field at the shema level. + // Custom OpenAPI field added by Kibana for a new field at the schema level. 'x-discontinued'?: string; } } diff --git a/packages/kbn-router-to-openapispec/src/__snapshots__/generate_oas.test.ts.snap b/packages/kbn-router-to-openapispec/src/__snapshots__/generate_oas.test.ts.snap index 818c0502ad774..fef935624ae64 100644 --- a/packages/kbn-router-to-openapispec/src/__snapshots__/generate_oas.test.ts.snap +++ b/packages/kbn-router-to-openapispec/src/__snapshots__/generate_oas.test.ts.snap @@ -572,6 +572,7 @@ OK response oas-test-version-2", }, "/no-xsrf/{id}/{path*}": Object { "post": Object { + "deprecated": true, "operationId": "%2Fno-xsrf%2F%7Bid%7D%2F%7Bpath*%7D#1", "parameters": Array [ Object { diff --git a/packages/kbn-router-to-openapispec/src/generate_oas.test.ts b/packages/kbn-router-to-openapispec/src/generate_oas.test.ts index 6db4237751217..25b786ac7c2c7 100644 --- a/packages/kbn-router-to-openapispec/src/generate_oas.test.ts +++ b/packages/kbn-router-to-openapispec/src/generate_oas.test.ts @@ -189,6 +189,7 @@ describe('generateOpenApiDocument', () => { versionedRouters: { testVersionedRouter: { routes: [{}] } }, bodySchema: createSharedZodSchema(), }); + expect( generateOpenApiDocument( { @@ -240,6 +241,7 @@ describe('generateOpenApiDocument', () => { { method: 'get', path: '/test', + isVersioned: true, options: { access: 'public' }, handlers: [ { @@ -321,4 +323,103 @@ describe('generateOpenApiDocument', () => { expect(result.paths['/v2-1']!.get!.tags).toEqual([]); }); }); + + describe('availability', () => { + it('creates the expected availability entries', () => { + const [routers, versionedRouters] = createTestRouters({ + routers: { + testRouter1: { + routes: [ + { + path: '/1-1/{id}/{path*}', + options: { availability: { stability: 'experimental' } }, + }, + { + path: '/1-2/{id}/{path*}', + options: { availability: { stability: 'beta' } }, + }, + { + path: '/1-3/{id}/{path*}', + options: { availability: { stability: 'stable' } }, + }, + ], + }, + testRouter2: { + routes: [{ path: '/2-1/{id}/{path*}' }], + }, + }, + versionedRouters: { + testVersionedRouter1: { + routes: [ + { + path: '/v1-1', + options: { + access: 'public', + options: { availability: { stability: 'experimental' } }, + }, + }, + { + path: '/v1-2', + options: { + access: 'public', + options: { availability: { stability: 'beta' } }, + }, + }, + { + path: '/v1-3', + options: { + access: 'public', + options: { availability: { stability: 'stable' } }, + }, + }, + ], + }, + testVersionedRouter2: { + routes: [{ path: '/v2-1', options: { access: 'public' } }], + }, + }, + }); + const result = generateOpenApiDocument( + { + routers, + versionedRouters, + }, + { + title: 'test', + baseUrl: 'https://test.oas', + version: '99.99.99', + } + ); + + // router paths + expect(result.paths['/1-1/{id}/{path*}']!.get).toMatchObject({ + 'x-state': 'Technical Preview', + }); + expect(result.paths['/1-2/{id}/{path*}']!.get).toMatchObject({ + 'x-state': 'Beta', + }); + + expect(result.paths['/1-3/{id}/{path*}']!.get).not.toMatchObject({ + 'x-state': expect.any(String), + }); + expect(result.paths['/2-1/{id}/{path*}']!.get).not.toMatchObject({ + 'x-state': expect.any(String), + }); + + // versioned router paths + expect(result.paths['/v1-1']!.get).toMatchObject({ + 'x-state': 'Technical Preview', + }); + expect(result.paths['/v1-2']!.get).toMatchObject({ + 'x-state': 'Beta', + }); + + expect(result.paths['/v1-3']!.get).not.toMatchObject({ + 'x-state': expect.any(String), + }); + expect(result.paths['/v2-1']!.get).not.toMatchObject({ + 'x-state': expect.any(String), + }); + }); + }); }); diff --git a/packages/kbn-router-to-openapispec/src/generate_oas.test.util.ts b/packages/kbn-router-to-openapispec/src/generate_oas.test.util.ts index 898f234cdc310..a39afb6357bfc 100644 --- a/packages/kbn-router-to-openapispec/src/generate_oas.test.util.ts +++ b/packages/kbn-router-to-openapispec/src/generate_oas.test.util.ts @@ -10,6 +10,7 @@ import type { ZodType } from '@kbn/zod'; import { schema, Type } from '@kbn/config-schema'; import type { CoreVersionedRouter, Router } from '@kbn/core-http-router-server-internal'; +import type { RouterRoute, VersionedRouterRoute } from '@kbn/core-http-server'; import { createLargeSchema } from './oas_converter/kbn_config_schema/lib.test.util'; type RoutesMeta = ReturnType[number]; @@ -27,7 +28,7 @@ export const createVersionedRouter = (args: { routes: VersionedRoutesMeta[] }) = } as unknown as CoreVersionedRouter; }; -export const getRouterDefaults = (bodySchema?: RuntimeSchema) => ({ +export const getRouterDefaults = (bodySchema?: RuntimeSchema): RouterRoute => ({ isVersioned: false, path: '/foo/{id}/{path*}', method: 'get', @@ -57,22 +58,29 @@ export const getRouterDefaults = (bodySchema?: RuntimeSchema) => ({ handler: jest.fn(), }); -export const getVersionedRouterDefaults = (bodySchema?: RuntimeSchema) => ({ +export const getVersionedRouterDefaults = (bodySchema?: RuntimeSchema): VersionedRouterRoute => ({ method: 'get', path: '/bar', options: { summary: 'versioned route', access: 'public', - deprecated: true, discontinued: 'route discontinued version or date', options: { tags: ['ignore-me', 'oas-tag:versioned'], }, }, + isVersioned: true, handlers: [ { fn: jest.fn(), options: { + options: { + deprecated: { + documentationUrl: 'https://fake-url', + reason: { type: 'remove' }, + severity: 'critical', + }, + }, validate: { request: { body: diff --git a/packages/kbn-router-to-openapispec/src/process_router.ts b/packages/kbn-router-to-openapispec/src/process_router.ts index 4437e35ea1f3e..f0d37fd208b7b 100644 --- a/packages/kbn-router-to-openapispec/src/process_router.ts +++ b/packages/kbn-router-to-openapispec/src/process_router.ts @@ -23,9 +23,11 @@ import { getVersionedHeaderParam, mergeResponseContent, prepareRoutes, + setXState, } from './util'; import type { OperationIdCounter } from './operation_id_counter'; import type { GenerateOpenApiDocumentOptionsFilters } from './generate_oas'; +import type { CustomOperationObject } from './type'; export const processRouter = ( appRouter: Router, @@ -61,11 +63,12 @@ export const processRouter = ( parameters.push(...pathObjects, ...queryObjects); } - const operation: OpenAPIV3.OperationObject = { + const hasDeprecations = !!route.options.deprecated; + const operation: CustomOperationObject = { summary: route.options.summary ?? '', tags: route.options.tags ? extractTags(route.options.tags) : [], ...(route.options.description ? { description: route.options.description } : {}), - ...(route.options.deprecated ? { deprecated: route.options.deprecated } : {}), + ...(hasDeprecations ? { deprecated: true } : {}), ...(route.options.discontinued ? { 'x-discontinued': route.options.discontinued } : {}), requestBody: !!validationSchemas?.body ? { @@ -81,6 +84,8 @@ export const processRouter = ( operationId: getOpId(route.path), }; + setXState(route.options.availability, operation); + const path: OpenAPIV3.PathItemObject = { [route.method]: operation, }; diff --git a/packages/kbn-router-to-openapispec/src/process_versioned_router.test.ts b/packages/kbn-router-to-openapispec/src/process_versioned_router.test.ts index 6452c2cf3c2cc..f9f4f4898c1d0 100644 --- a/packages/kbn-router-to-openapispec/src/process_versioned_router.test.ts +++ b/packages/kbn-router-to-openapispec/src/process_versioned_router.test.ts @@ -8,10 +8,7 @@ */ import { schema } from '@kbn/config-schema'; -import type { - CoreVersionedRouter, - VersionedRouterRoute, -} from '@kbn/core-http-router-server-internal'; +import type { CoreVersionedRouter } from '@kbn/core-http-router-server-internal'; import { get } from 'lodash'; import { OasConverter } from './oas_converter'; import { createOperationIdCounter } from './operation_id_counter'; @@ -20,6 +17,7 @@ import { extractVersionedResponses, extractVersionedRequestBodies, } from './process_versioned_router'; +import { VersionedRouterRoute } from '@kbn/core-http-server'; let oasConverter: OasConverter; beforeEach(() => { @@ -151,6 +149,7 @@ describe('processVersionedRouter', () => { const createTestRoute: () => VersionedRouterRoute = () => ({ path: '/foo', method: 'get', + isVersioned: true, options: { access: 'public', deprecated: true, diff --git a/packages/kbn-router-to-openapispec/src/process_versioned_router.ts b/packages/kbn-router-to-openapispec/src/process_versioned_router.ts index 97b92f92fde57..7eee0d20c11d2 100644 --- a/packages/kbn-router-to-openapispec/src/process_versioned_router.ts +++ b/packages/kbn-router-to-openapispec/src/process_versioned_router.ts @@ -10,10 +10,9 @@ import { type CoreVersionedRouter, versionHandlerResolvers, - VersionedRouterRoute, unwrapVersionedResponseBodyValidation, } from '@kbn/core-http-router-server-internal'; -import type { RouteMethod } from '@kbn/core-http-server'; +import type { RouteMethod, VersionedRouterRoute } from '@kbn/core-http-server'; import type { OpenAPIV3 } from 'openapi-types'; import type { GenerateOpenApiDocumentOptionsFilters } from './generate_oas'; import type { OasConverter } from './oas_converter'; @@ -29,6 +28,7 @@ import { extractTags, mergeResponseContent, getXsrfHeaderForMethod, + setXState, } from './util'; export const processVersionedRouter = ( @@ -93,11 +93,13 @@ export const processVersionedRouter = ( const hasBody = Boolean(extractValidationSchemaFromVersionedHandler(handler)?.request?.body); const contentType = extractContentType(route.options.options?.body); const hasVersionFilter = Boolean(filters?.version); + // If any handler is deprecated we show deprecated: true in the spec + const hasDeprecations = route.handlers.some(({ options }) => !!options.options?.deprecated); const operation: OpenAPIV3.OperationObject = { summary: route.options.summary ?? '', tags: route.options.options?.tags ? extractTags(route.options.options.tags) : [], ...(route.options.description ? { description: route.options.description } : {}), - ...(route.options.deprecated ? { deprecated: route.options.deprecated } : {}), + ...(hasDeprecations ? { deprecated: true } : {}), ...(route.options.discontinued ? { 'x-discontinued': route.options.discontinued } : {}), requestBody: hasBody ? { @@ -112,6 +114,9 @@ export const processVersionedRouter = ( parameters, operationId: getOpId(route.path), }; + + setXState(route.options.options?.availability, operation); + const path: OpenAPIV3.PathItemObject = { [route.method]: operation, }; diff --git a/packages/kbn-router-to-openapispec/src/type.ts b/packages/kbn-router-to-openapispec/src/type.ts index 09dc247e5a5c9..5c5f992a0de0f 100644 --- a/packages/kbn-router-to-openapispec/src/type.ts +++ b/packages/kbn-router-to-openapispec/src/type.ts @@ -34,3 +34,8 @@ export interface OpenAPIConverter { is(type: unknown): boolean; } + +export type CustomOperationObject = OpenAPIV3.OperationObject<{ + // Custom OpenAPI from ES API spec based on @availability + 'x-state'?: 'Technical Preview' | 'Beta'; +}>; diff --git a/packages/kbn-router-to-openapispec/src/util.ts b/packages/kbn-router-to-openapispec/src/util.ts index 55f7348dc199a..beefbebc0aec7 100644 --- a/packages/kbn-router-to-openapispec/src/util.ts +++ b/packages/kbn-router-to-openapispec/src/util.ts @@ -17,7 +17,7 @@ import { type RouterRoute, type RouteValidatorConfig, } from '@kbn/core-http-server'; -import { KnownParameters } from './type'; +import { CustomOperationObject, KnownParameters } from './type'; import type { GenerateOpenApiDocumentOptionsFilters } from './generate_oas'; const tagPrefix = 'oas-tag:'; @@ -165,3 +165,17 @@ export const getXsrfHeaderForMethod = ( }, ]; }; + +export function setXState( + availability: RouteConfigOptions['availability'], + operation: CustomOperationObject +): void { + if (availability) { + if (availability.stability === 'experimental') { + operation['x-state'] = 'Technical Preview'; + } + if (availability.stability === 'beta') { + operation['x-state'] = 'Beta'; + } + } +} diff --git a/packages/kbn-search-index-documents/lib/fetch_search_results.test.ts b/packages/kbn-search-index-documents/lib/fetch_search_results.test.ts index 470f8ba602ebc..a47c351a00180 100644 --- a/packages/kbn-search-index-documents/lib/fetch_search_results.test.ts +++ b/packages/kbn-search-index-documents/lib/fetch_search_results.test.ts @@ -88,6 +88,7 @@ describe('fetchSearchResults lib function', () => { index: indexName, q: query, size: DEFAULT_DOCS_PER_PAGE, + track_total_hits: false, }); }); @@ -109,6 +110,7 @@ describe('fetchSearchResults lib function', () => { index: indexName, q: '\\"yellow banana\\"', size: DEFAULT_DOCS_PER_PAGE, + track_total_hits: false, }); }); @@ -123,6 +125,7 @@ describe('fetchSearchResults lib function', () => { from: DEFAULT_FROM_VALUE, index: indexName, size: DEFAULT_DOCS_PER_PAGE, + track_total_hits: false, }); }); @@ -150,6 +153,42 @@ describe('fetchSearchResults lib function', () => { index: indexName, q: query, size: DEFAULT_DOCS_PER_PAGE, + track_total_hits: false, + }); + }); + + it('should send track_total_hits true when specified', async () => { + mockClient.search.mockImplementationOnce(() => + Promise.resolve({ + ...mockSearchResponseWithHits, + hits: { + ...mockSearchResponseWithHits.hits, + total: { + ...mockSearchResponseWithHits.hits.total, + value: 0, + }, + hits: [], + }, + }) + ); + + await expect( + fetchSearchResults( + mockClient as unknown as ElasticsearchClient, + indexName, + query, + 0, + 25, + true + ) + ).resolves.toEqual(emptySearchResultsResponse); + + expect(mockClient.search).toHaveBeenCalledWith({ + from: DEFAULT_FROM_VALUE, + index: indexName, + q: query, + size: DEFAULT_DOCS_PER_PAGE, + track_total_hits: true, }); }); }); diff --git a/packages/kbn-search-index-documents/lib/fetch_search_results.ts b/packages/kbn-search-index-documents/lib/fetch_search_results.ts index c5cefdf67ed9d..1831920f5d4c1 100644 --- a/packages/kbn-search-index-documents/lib/fetch_search_results.ts +++ b/packages/kbn-search-index-documents/lib/fetch_search_results.ts @@ -18,7 +18,8 @@ export const fetchSearchResults = async ( indexName: string, query?: string, from: number = 0, - size: number = DEFAULT_DOCS_PER_PAGE + size: number = DEFAULT_DOCS_PER_PAGE, + trackTotalHits: boolean = false ): Promise> => { const result = await fetchWithPagination( async () => @@ -27,6 +28,7 @@ export const fetchSearchResults = async ( index: indexName, size, ...(!!query ? { q: escapeLuceneChars(query) } : {}), + track_total_hits: trackTotalHits, }), from, size diff --git a/packages/kbn-test/src/auth/saml_auth.test.ts b/packages/kbn-test/src/auth/saml_auth.test.ts index 47c4724a8b6a3..aee096bf141d6 100644 --- a/packages/kbn-test/src/auth/saml_auth.test.ts +++ b/packages/kbn-test/src/auth/saml_auth.test.ts @@ -168,6 +168,36 @@ describe('saml_auth', () => { 'Failed to create the new cloud session, check retry arguments: {"attemptsCount":0,"attemptDelay":100}' ); }); + + test(`should fail without retry when response has 'mfa_required: true'`, async () => { + axiosRequestMock.mockImplementation((config: AxiosRequestConfig) => { + if (config.url?.endsWith('/api/v1/saas/auth/_login')) { + return Promise.resolve({ + data: { user_id: 12345, authenticated: false, mfa_required: true }, + status: 200, + }); + } + return Promise.reject(new Error(`Unexpected URL: ${config.url}`)); + }); + + await expect( + createCloudSession( + { + hostname: 'cloud', + email: 'viewer@elastic.co', + password: 'changeme', + log, + }, + { + attemptsCount: 3, + attemptDelay: 100, + } + ) + ).rejects.toThrow( + 'Failed to create the new cloud session: MFA must be disabled for the test account' + ); + expect(axiosRequestMock).toBeCalledTimes(1); + }); }); describe('createSAMLRequest', () => { diff --git a/packages/kbn-test/src/auth/saml_auth.ts b/packages/kbn-test/src/auth/saml_auth.ts index 0ec264d66d425..968788371c827 100644 --- a/packages/kbn-test/src/auth/saml_auth.ts +++ b/packages/kbn-test/src/auth/saml_auth.ts @@ -134,7 +134,17 @@ export const createCloudSession = async ( data[key] = 'REDACTED'; } }); + + // MFA must be disabled for test accounts + if (data.mfa_required === true) { + // Changing MFA configuration requires manual action, skip retry + attemptsLeft = 0; + throw new Error( + `Failed to create the new cloud session: MFA must be disabled for the test account` + ); + } } + throw new Error( `Failed to create the new cloud session: token is missing in response data\n${JSON.stringify( data diff --git a/packages/response-ops/rule_params/README.md b/packages/response-ops/rule_params/README.md new file mode 100644 index 0000000000000..8cc747bf38864 --- /dev/null +++ b/packages/response-ops/rule_params/README.md @@ -0,0 +1,3 @@ +# @kbn/response-ops-rule-params + +The package is responsible for the parameters' schema of all rule types. The alerting plugin uses this package to generate OAS documentation for the `params` property in the rule in requests and responses. diff --git a/packages/response-ops/rule_params/index.ts b/packages/response-ops/rule_params/index.ts new file mode 100644 index 0000000000000..a5ce640a4c5d4 --- /dev/null +++ b/packages/response-ops/rule_params/index.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +export { ruleParamsSchema, ruleParamsSchemaWithDefaultValue } from './latest'; + +export { + ruleParamsSchema as ruleParamsSchemaV1, + ruleParamsSchemaWithDefaultValue as ruleParamsSchemaWithDefaultValueV1, +} from './v1'; + +export type { RuleParams } from './latest'; +export type { RuleParamsWithDefaultValue } from './latest'; + +export type { + RuleParams as RuleParamsV1, + RuleParamsWithDefaultValue as RuleParamsWithDefaultValueV1, +} from './v1'; diff --git a/packages/response-ops/rule_params/jest.config.js b/packages/response-ops/rule_params/jest.config.js new file mode 100644 index 0000000000000..ee60f7ea42272 --- /dev/null +++ b/packages/response-ops/rule_params/jest.config.js @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../../..', + roots: ['/packages/response-ops/rule_params'], +}; diff --git a/packages/response-ops/rule_params/kibana.jsonc b/packages/response-ops/rule_params/kibana.jsonc new file mode 100644 index 0000000000000..6a6744a58c4a1 --- /dev/null +++ b/packages/response-ops/rule_params/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/response-ops-rule-params", + "owner": "@elastic/response-ops" +} diff --git a/packages/response-ops/rule_params/latest.ts b/packages/response-ops/rule_params/latest.ts new file mode 100644 index 0000000000000..f278309c22b03 --- /dev/null +++ b/packages/response-ops/rule_params/latest.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +export * from './v1'; diff --git a/packages/response-ops/rule_params/package.json b/packages/response-ops/rule_params/package.json new file mode 100644 index 0000000000000..43145b8d5da4c --- /dev/null +++ b/packages/response-ops/rule_params/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/response-ops-rule-params", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0" +} \ No newline at end of file diff --git a/packages/response-ops/rule_params/tsconfig.json b/packages/response-ops/rule_params/tsconfig.json new file mode 100644 index 0000000000000..3df73f778fdc1 --- /dev/null +++ b/packages/response-ops/rule_params/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/config-schema", + ] +} diff --git a/packages/response-ops/rule_params/v1.ts b/packages/response-ops/rule_params/v1.ts new file mode 100644 index 0000000000000..a083f67f10c8c --- /dev/null +++ b/packages/response-ops/rule_params/v1.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { TypeOf, schema } from '@kbn/config-schema'; + +export const ruleParamsSchema = schema.recordOf(schema.string(), schema.maybe(schema.any()), { + meta: { description: 'The parameters for the rule.' }, +}); + +export const ruleParamsSchemaWithDefaultValue = schema.recordOf( + schema.string(), + schema.maybe(schema.any()), + { + defaultValue: {}, + meta: { description: 'The parameters for the rule.' }, + } +); + +export type RuleParams = TypeOf; +export type RuleParamsWithDefaultValue = TypeOf; diff --git a/scripts/manifest.js b/scripts/manifest.js new file mode 100644 index 0000000000000..f9da9c3d174bd --- /dev/null +++ b/scripts/manifest.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +require('../src/setup_node_env'); +require('@kbn/manifest').runKbnManifestCli(); diff --git a/src/core/server/index.ts b/src/core/server/index.ts index 1ac38b1d44157..52149cd611be3 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -242,7 +242,11 @@ export type { } from '@kbn/core-http-server'; export type { IExternalUrlPolicy } from '@kbn/core-http-common'; -export { validBodyOutput, OnPostAuthResultType } from '@kbn/core-http-server'; +export { + validBodyOutput, + OnPostAuthResultType, + ReservedPrivilegesSet, +} from '@kbn/core-http-server'; export type { HttpResourcesRenderOptions, diff --git a/src/core/server/integration_tests/http/request_representation.test.ts b/src/core/server/integration_tests/http/request_representation.test.ts index f180a3a49ce0f..82300eceec774 100644 --- a/src/core/server/integration_tests/http/request_representation.test.ts +++ b/src/core/server/integration_tests/http/request_representation.test.ts @@ -87,6 +87,7 @@ describe('request logging', () => { route: { method: 'get', path: '/', + routePath: '/', options: expect.any(Object), }, uuid: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', @@ -116,10 +117,12 @@ describe('request logging', () => { auth: { isAuthenticated: false }, route: { path: '/', + routePath: '/', method: 'get', options: { authRequired: true, xsrfRequired: false, + deprecated: undefined, access: 'internal', tags: [], security: undefined, @@ -127,7 +130,8 @@ describe('request logging', () => { body: undefined } }, - authzResult: undefined + authzResult: undefined, + apiVersion: undefined }" `); }); diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_xy_args.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_xy_args.ts index f9f38e19b7396..4be72d333d781 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_xy_args.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_xy_args.ts @@ -40,7 +40,7 @@ export const commonXYArgs: CommonXYFn['args'] = { }, emphasizeFitting: { types: ['boolean'], - default: false, + default: true, help: '', }, valueLabels: { diff --git a/src/plugins/chart_expressions/expression_xy/common/index.ts b/src/plugins/chart_expressions/expression_xy/common/index.ts index dc7c1a7c6334b..6b464a40e87db 100755 --- a/src/plugins/chart_expressions/expression_xy/common/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/index.ts @@ -10,7 +10,7 @@ export const PLUGIN_ID = 'expressionXy'; export const PLUGIN_NAME = 'expressionXy'; -export { LayerTypes, XYCurveTypes } from './constants'; +export { LayerTypes, XYCurveTypes, FittingFunctions } from './constants'; export type { AllowedXYOverrides, diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx index e45edccba2779..473562b63ad5e 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx @@ -100,7 +100,6 @@ type GetColorFn = ( type GetPointConfigFn = (config: { xAccessor: string | undefined; markSizeAccessor: string | undefined; - emphasizeFitting?: boolean; showPoints?: boolean; pointsRadius?: number; }) => Partial; @@ -297,18 +296,10 @@ export const getSeriesName: GetSeriesNameFn = ( return splitValues.length > 0 ? splitValues.join(' - ') : yAccessorTitle; }; -const getPointConfig: GetPointConfigFn = ({ - xAccessor, - markSizeAccessor, - emphasizeFitting, - showPoints, - pointsRadius, -}) => { +const getPointConfig: GetPointConfigFn = ({ markSizeAccessor, showPoints, pointsRadius }) => { return { - visible: (showPoints !== undefined ? showPoints : !xAccessor || markSizeAccessor !== undefined) - ? 'always' - : 'never', - radius: pointsRadius !== undefined ? pointsRadius : xAccessor && !emphasizeFitting ? 5 : 0, + visible: showPoints || markSizeAccessor ? 'always' : 'never', + radius: pointsRadius, fill: markSizeAccessor ? ColorVariant.Series : undefined, }; }; @@ -550,7 +541,6 @@ export const getSeriesProps: GetSeriesPropsFn = ({ point: getPointConfig({ xAccessor: xColumnId, markSizeAccessor: markSizeColumnId, - emphasizeFitting, showPoints: layer.showPoints, pointsRadius: layer.pointsRadius, }), @@ -567,7 +557,6 @@ export const getSeriesProps: GetSeriesPropsFn = ({ point: getPointConfig({ xAccessor: xColumnId, markSizeAccessor: markSizeColumnId, - emphasizeFitting, showPoints: layer.showPoints, pointsRadius: layer.pointsRadius, }), diff --git a/src/plugins/chart_expressions/expression_xy/public/index.ts b/src/plugins/chart_expressions/expression_xy/public/index.ts index a6e001d5eb5b1..d63ac6c2a5930 100755 --- a/src/plugins/chart_expressions/expression_xy/public/index.ts +++ b/src/plugins/chart_expressions/expression_xy/public/index.ts @@ -15,6 +15,6 @@ export function plugin() { return new ExpressionXyPlugin(); } -export { LayerTypes, XYCurveTypes } from '../common'; +export { LayerTypes, XYCurveTypes, FittingFunctions } from '../common'; export type { ExpressionXyPluginSetup, ExpressionXyPluginStart } from './types'; diff --git a/src/plugins/console/public/application/containers/editor/hooks/use_set_initial_value.test.ts b/src/plugins/console/public/application/containers/editor/hooks/use_set_initial_value.test.ts new file mode 100644 index 0000000000000..0605a0c903ee0 --- /dev/null +++ b/src/plugins/console/public/application/containers/editor/hooks/use_set_initial_value.test.ts @@ -0,0 +1,178 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { renderHook, act } from '@testing-library/react-hooks'; +import { useSetInitialValue } from './use_set_initial_value'; +import { IToasts } from '@kbn/core-notifications-browser'; +import { decompressFromEncodedURIComponent } from 'lz-string'; +import { DEFAULT_INPUT_VALUE } from '../../../../../common/constants'; + +jest.mock('lz-string', () => ({ + decompressFromEncodedURIComponent: jest.fn(), +})); + +jest.mock('./use_set_initial_value', () => ({ + ...jest.requireActual('./use_set_initial_value'), +})); + +describe('useSetInitialValue', () => { + const setValueMock = jest.fn(); + const addWarningMock = jest.fn(); + const toastsMock: IToasts = { addWarning: addWarningMock } as any; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should set the initial value only once', async () => { + const { rerender } = renderHook(() => + useSetInitialValue({ + localStorageValue: 'initial value', + setValue: setValueMock, + toasts: toastsMock, + }) + ); + + // Verify initial value is set on first render + expect(setValueMock).toHaveBeenCalledTimes(1); + expect(setValueMock).toHaveBeenCalledWith('initial value'); + + // Re-render the hook to simulate a component update + rerender(); + + // Verify that setValue is not called again after rerender + expect(setValueMock).toHaveBeenCalledTimes(1); // Still 1, no additional calls + }); + + it('should set value from localStorage if no load_from param is present', () => { + renderHook(() => + useSetInitialValue({ + localStorageValue: 'saved value', + setValue: setValueMock, + toasts: toastsMock, + }) + ); + + expect(setValueMock).toHaveBeenCalledWith('saved value'); + }); + + it('should set default value if localStorage is undefined and no load_from param is present', () => { + renderHook(() => + useSetInitialValue({ + localStorageValue: undefined, + setValue: setValueMock, + toasts: toastsMock, + }) + ); + + expect(setValueMock).toHaveBeenCalledWith(DEFAULT_INPUT_VALUE); + }); + + it('should load data from load_from param if it is a valid Elastic URL', async () => { + Object.defineProperty(window, 'location', { + writable: true, + value: { + hash: '?load_from=https://www.elastic.co/some-data', + }, + }); + + // Mock fetch to return "remote data" + global.fetch = jest.fn(() => + Promise.resolve({ + text: () => Promise.resolve('remote data'), + }) + ) as jest.Mock; + + await act(async () => { + renderHook(() => + useSetInitialValue({ + localStorageValue: 'initial value', + setValue: setValueMock, + toasts: toastsMock, + }) + ); + }); + + expect(fetch).toHaveBeenCalledWith(new URL('https://www.elastic.co/some-data')); + // The remote data should be appended to the initial value in the editor + expect(setValueMock).toHaveBeenCalledWith('initial value\n\nremote data'); + }); + + it('should show a warning if the load_from param is not an Elastic domain', async () => { + Object.defineProperty(window, 'location', { + writable: true, + value: { + hash: '?load_from=https://not.elastic.com/some-data', + }, + }); + + await act(async () => { + renderHook(() => + useSetInitialValue({ + localStorageValue: 'initial value', + setValue: setValueMock, + toasts: toastsMock, + }) + ); + }); + + expect(fetch).not.toHaveBeenCalled(); + expect(addWarningMock).toHaveBeenCalledWith( + 'Only URLs with the Elastic domain (www.elastic.co) can be loaded in Console.' + ); + }); + + it('should load and decompress data from a data URI', async () => { + Object.defineProperty(window, 'location', { + writable: true, + value: { + hash: '?load_from=data:text/plain,compressed-data', + }, + }); + (decompressFromEncodedURIComponent as jest.Mock).mockReturnValue('decompressed data'); + + await act(async () => { + renderHook(() => + useSetInitialValue({ + localStorageValue: 'initial value', + setValue: setValueMock, + toasts: toastsMock, + }) + ); + }); + + expect(decompressFromEncodedURIComponent).toHaveBeenCalledWith('compressed-data'); + // The initial value in the editor should be replaces with the decompressed data + expect(setValueMock).toHaveBeenCalledWith('decompressed data'); + }); + + it('should show a warning if decompressing a data URI fails', async () => { + Object.defineProperty(window, 'location', { + writable: true, + value: { + hash: '?load_from=data:text/plain,invalid-data', + }, + }); + (decompressFromEncodedURIComponent as jest.Mock).mockReturnValue(null); + + await act(async () => { + renderHook(() => + useSetInitialValue({ + localStorageValue: 'initial value', + setValue: setValueMock, + toasts: toastsMock, + }) + ); + }); + + expect(addWarningMock).toHaveBeenCalledWith( + 'Unable to load data from the load_from query parameter in the URL' + ); + }); +}); diff --git a/src/plugins/console/public/application/containers/editor/hooks/use_set_initial_value.ts b/src/plugins/console/public/application/containers/editor/hooks/use_set_initial_value.ts index 9e96c1af2734b..11e9c4878227e 100644 --- a/src/plugins/console/public/application/containers/editor/hooks/use_set_initial_value.ts +++ b/src/plugins/console/public/application/containers/editor/hooks/use_set_initial_value.ts @@ -12,7 +12,7 @@ import { parse } from 'query-string'; import { IToasts } from '@kbn/core-notifications-browser'; import { decompressFromEncodedURIComponent } from 'lz-string'; import { i18n } from '@kbn/i18n'; -import { useEffect } from 'react'; +import { useEffect, useRef } from 'react'; import { DEFAULT_INPUT_VALUE } from '../../../../../common/constants'; interface QueryParams { @@ -46,6 +46,7 @@ export const readLoadFromParam = () => { */ export const useSetInitialValue = (params: SetInitialValueParams) => { const { localStorageValue, setValue, toasts } = params; + const isInitialValueSet = useRef(false); useEffect(() => { const loadBufferFromRemote = async (url: string) => { @@ -104,11 +105,15 @@ export const useSetInitialValue = (params: SetInitialValueParams) => { const loadFromParam = readLoadFromParam(); - if (loadFromParam) { - loadBufferFromRemote(loadFromParam); - } else { - // Only set to default input value if the localstorage value is undefined - setValue(localStorageValue ?? DEFAULT_INPUT_VALUE); + // Only set the value in the editor if an initial value hasn't been set yet + if (!isInitialValueSet.current) { + if (loadFromParam) { + loadBufferFromRemote(loadFromParam); + } else { + // Only set to default input value if the localstorage value is undefined + setValue(localStorageValue ?? DEFAULT_INPUT_VALUE); + } + isInitialValueSet.current = true; } return () => { diff --git a/src/plugins/dashboard/kibana.jsonc b/src/plugins/dashboard/kibana.jsonc index 2bf60cde55ef0..d7b0f2c16e04b 100644 --- a/src/plugins/dashboard/kibana.jsonc +++ b/src/plugins/dashboard/kibana.jsonc @@ -24,7 +24,7 @@ "urlForwarding", "presentationUtil", "visualizations", - "unifiedSearch" + "unifiedSearch", ], "optionalPlugins": [ "home", @@ -35,7 +35,8 @@ "taskManager", "serverless", "noDataPage", - "observabilityAIAssistant" + "observabilityAIAssistant", + "lens" ], "requiredBundles": [ "kibanaReact", diff --git a/src/plugins/dashboard/public/dashboard_app/no_data/dashboard_app_no_data.tsx b/src/plugins/dashboard/public/dashboard_app/no_data/dashboard_app_no_data.tsx index 15b3d00d07ec7..366726267c311 100644 --- a/src/plugins/dashboard/public/dashboard_app/no_data/dashboard_app_no_data.tsx +++ b/src/plugins/dashboard/public/dashboard_app/no_data/dashboard_app_no_data.tsx @@ -7,9 +7,19 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import React from 'react'; - +import React, { useCallback, useEffect, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import useAsync from 'react-use/lib/useAsync'; +import { v4 as uuidv4 } from 'uuid'; +import { + getESQLAdHocDataview, + getESQLQueryColumns, + getIndexForESQLQuery, + getInitialESQLQuery, +} from '@kbn/esql-utils'; import { withSuspense } from '@kbn/shared-ux-utility'; +import type { TypedLensByValueInput } from '@kbn/lens-plugin/public'; +import { getLensAttributesFromSuggestion } from '@kbn/visualization-utils'; import { DASHBOARD_APP_ID } from '../../dashboard_constants'; import { @@ -19,10 +29,15 @@ import { embeddableService, noDataPageService, shareService, + lensService, } from '../../services/kibana_services'; import { getDashboardBackupService } from '../../services/dashboard_backup_service'; import { getDashboardContentManagementService } from '../../services/dashboard_content_management_service'; +function generateId() { + return uuidv4(); +} + export const DashboardAppNoDataPage = ({ onDataViewCreated, }: { @@ -35,7 +50,7 @@ export const DashboardAppNoDataPage = ({ noDataPage: noDataPageService, share: shareService, }; - + const [abortController, setAbortController] = useState(new AbortController()); const importPromise = import('@kbn/shared-ux-page-analytics-no-data'); const AnalyticsNoDataPageKibanaProvider = withSuspense( React.lazy(() => @@ -44,6 +59,83 @@ export const DashboardAppNoDataPage = ({ }) ) ); + + const lensHelpersAsync = useAsync(() => { + return lensService?.stateHelperApi() ?? Promise.resolve(null); + }, [lensService]); + + useEffect(() => { + return () => { + abortController?.abort(); + }; + }, [abortController]); + + const onTryESQL = useCallback(async () => { + abortController?.abort(); + if (lensHelpersAsync.value) { + const abc = new AbortController(); + const { dataViews } = dataService; + const indexName = (await getIndexForESQLQuery({ dataViews })) ?? '*'; + const dataView = await getESQLAdHocDataview(`from ${indexName}`, dataViews); + const esqlQuery = getInitialESQLQuery(dataView); + + try { + const columns = await getESQLQueryColumns({ + esqlQuery, + search: dataService.search.search, + signal: abc.signal, + timeRange: dataService.query.timefilter.timefilter.getAbsoluteTime(), + }); + + // lens suggestions api context + const context = { + dataViewSpec: dataView?.toSpec(false), + fieldName: '', + textBasedColumns: columns, + query: { esql: esqlQuery }, + }; + + setAbortController(abc); + + const chartSuggestions = lensHelpersAsync.value.suggestions(context, dataView); + if (chartSuggestions?.length) { + const [suggestion] = chartSuggestions; + + const attrs = getLensAttributesFromSuggestion({ + filters: [], + query: { + esql: esqlQuery, + }, + suggestion, + dataView, + }) as TypedLensByValueInput['attributes']; + + const lensEmbeddableInput = { + attributes: attrs, + id: generateId(), + }; + + await embeddableService.getStateTransfer().navigateToWithEmbeddablePackage('dashboards', { + state: { + type: 'lens', + input: lensEmbeddableInput, + }, + path: '#/create', + }); + } + } catch (error) { + if (error.name !== 'AbortError') { + coreServices.notifications.toasts.addWarning( + i18n.translate('dashboard.noDataviews.esqlRequestWarningMessage', { + defaultMessage: 'Unable to load columns. {errorMessage}', + values: { errorMessage: error.message }, + }) + ); + } + } + } + }, [abortController, lensHelpersAsync.value]); + const AnalyticsNoDataPage = withSuspense( React.lazy(() => importPromise.then(({ AnalyticsNoDataPage: NoDataPage }) => { @@ -54,7 +146,7 @@ export const DashboardAppNoDataPage = ({ return ( - + ); }; diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index b7a920eb08ce3..a8e7cd96f38db 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -27,6 +27,7 @@ import { type CoreStart, } from '@kbn/core/public'; import type { DataPublicPluginSetup, DataPublicPluginStart } from '@kbn/data-plugin/public'; +import type { LensPublicSetup, LensPublicStart } from '@kbn/lens-plugin/public'; import type { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public'; import type { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public'; import { FieldFormatsStart } from '@kbn/field-formats-plugin/public/plugin'; @@ -96,6 +97,7 @@ export interface DashboardSetupDependencies { urlForwarding: UrlForwardingSetup; unifiedSearch: UnifiedSearchPublicPluginStart; observabilityAIAssistant?: ObservabilityAIAssistantPublicSetup; + lens?: LensPublicSetup; } export interface DashboardStartDependencies { @@ -120,6 +122,7 @@ export interface DashboardStartDependencies { customBranding: CustomBrandingStart; serverless?: ServerlessPluginStart; noDataPage?: NoDataPagePluginStart; + lens?: LensPublicStart; observabilityAIAssistant?: ObservabilityAIAssistantPublicStart; } diff --git a/src/plugins/dashboard/public/services/kibana_services.ts b/src/plugins/dashboard/public/services/kibana_services.ts index e8b164d47b413..e3fde8c37c2a9 100644 --- a/src/plugins/dashboard/public/services/kibana_services.ts +++ b/src/plugins/dashboard/public/services/kibana_services.ts @@ -18,6 +18,7 @@ import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public/plugin' import type { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public'; import type { NoDataPagePluginStart } from '@kbn/no-data-page-plugin/public'; import type { ObservabilityAIAssistantPublicStart } from '@kbn/observability-ai-assistant-plugin/public'; +import type { LensPublicStart } from '@kbn/lens-plugin/public'; import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; import type { SavedObjectTaggingOssPluginStart } from '@kbn/saved-objects-tagging-oss-plugin/public'; import type { ScreenshotModePluginStart } from '@kbn/screenshot-mode-plugin/public'; @@ -40,6 +41,7 @@ export let fieldFormatService: FieldFormatsStart; export let navigationService: NavigationPublicPluginStart; export let noDataPageService: NoDataPagePluginStart | undefined; export let observabilityAssistantService: ObservabilityAIAssistantPublicStart | undefined; +export let lensService: LensPublicStart | undefined; export let presentationUtilService: PresentationUtilPluginStart; export let savedObjectsTaggingService: SavedObjectTaggingOssPluginStart | undefined; export let screenshotModeService: ScreenshotModePluginStart; @@ -63,6 +65,7 @@ export const setKibanaServices = (kibanaCore: CoreStart, deps: DashboardStartDep navigationService = deps.navigation; noDataPageService = deps.noDataPage; observabilityAssistantService = deps.observabilityAIAssistant; + lensService = deps.lens; presentationUtilService = deps.presentationUtil; savedObjectsTaggingService = deps.savedObjectsTaggingOss; serverlessService = deps.serverless; diff --git a/src/plugins/dashboard/tsconfig.json b/src/plugins/dashboard/tsconfig.json index 57125918ef3fc..3e95675ea64c3 100644 --- a/src/plugins/dashboard/tsconfig.json +++ b/src/plugins/dashboard/tsconfig.json @@ -80,6 +80,7 @@ "@kbn/content-management-favorites-public", "@kbn/core-custom-branding-browser-mocks", "@kbn/core-mount-utils-browser", + "@kbn/visualization-utils", ], "exclude": ["target/**/*"] } diff --git a/src/plugins/data/common/search/aggs/buckets/date_histogram.ts b/src/plugins/data/common/search/aggs/buckets/date_histogram.ts index 66820ff0d3e94..314ee4d03042c 100644 --- a/src/plugins/data/common/search/aggs/buckets/date_histogram.ts +++ b/src/plugins/data/common/search/aggs/buckets/date_histogram.ts @@ -116,7 +116,14 @@ export const getDateHistogramBucketAgg = ({ dateFormat: getConfig('dateFormat'), 'dateFormat:scaled': getConfig('dateFormat:scaled'), }); - updateTimeBuckets(this, calculateBounds, buckets); + + try { + updateTimeBuckets(this, calculateBounds, buckets); + } catch (e) { + // swallow the error even though the agg is misconfigured + // eslint-disable-next-line no-console + console.error(e); + } return buckets; }, diff --git a/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.ts b/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.ts index 1ebeb8d7d1b7f..66b5be4d02ab7 100644 --- a/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.ts +++ b/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.ts @@ -61,7 +61,7 @@ export const useDiscoverHistogram = ({ hideChart, }: UseDiscoverHistogramProps) => { const services = useDiscoverServices(); - const savedSearchData$ = stateContainer.dataState.data$; + const { main$, documents$, totalHits$ } = stateContainer.dataState.data$; const savedSearchState = useSavedSearch(); const isEsqlMode = useIsEsqlMode(); @@ -153,10 +153,7 @@ export const useDiscoverHistogram = ({ * Total hits */ - const setTotalHitsError = useMemo( - () => sendErrorTo(savedSearchData$.totalHits$), - [savedSearchData$.totalHits$] - ); + const setTotalHitsError = useMemo(() => sendErrorTo(totalHits$), [totalHits$]); useEffect(() => { const subscription = createTotalHitsObservable(unifiedHistogram?.state$)?.subscribe( @@ -172,7 +169,7 @@ export const useDiscoverHistogram = ({ return; } - const { result: totalHitsResult } = savedSearchData$.totalHits$.getValue(); + const { result: totalHitsResult } = totalHits$.getValue(); if ( (status === UnifiedHistogramFetchStatus.loading || @@ -184,18 +181,22 @@ export const useDiscoverHistogram = ({ return; } - // Sync the totalHits$ observable with the unified histogram state - savedSearchData$.totalHits$.next({ - fetchStatus: status.toString() as FetchStatus, - result, - }); + const fetchStatus = status.toString() as FetchStatus; + + // Do not sync the loading state since it's already handled by fetchAll + if (fetchStatus !== FetchStatus.LOADING) { + totalHits$.next({ + fetchStatus, + result, + }); + } if (status !== UnifiedHistogramFetchStatus.complete || typeof result !== 'number') { return; } // Check the hits count to set a partial or no results state - checkHitCount(savedSearchData$.main$, result); + checkHitCount(main$, result); } ); @@ -204,8 +205,8 @@ export const useDiscoverHistogram = ({ }; }, [ isEsqlMode, - savedSearchData$.main$, - savedSearchData$.totalHits$, + main$, + totalHits$, setTotalHitsError, stateContainer.appState, unifiedHistogram?.state$, @@ -234,7 +235,7 @@ export const useDiscoverHistogram = ({ const [initialEsqlProps] = useState(() => getUnifiedHistogramPropsForEsql({ - documentsValue: savedSearchData$.documents$.getValue(), + documentsValue: documents$.getValue(), savedSearch: stateContainer.savedSearchState.getState(), }) ); diff --git a/src/plugins/discover/public/application/main/data_fetching/fetch_all.ts b/src/plugins/discover/public/application/main/data_fetching/fetch_all.ts index 660dff3bdb4ff..3b54e6f8ce083 100644 --- a/src/plugins/discover/public/application/main/data_fetching/fetch_all.ts +++ b/src/plugins/discover/public/application/main/data_fetching/fetch_all.ts @@ -92,13 +92,9 @@ export function fetchAll( // Mark all subjects as loading sendLoadingMsg(dataSubjects.main$); sendLoadingMsg(dataSubjects.documents$, { query }); - - // histogram for data view mode will send `loading` for totalHits$ - if (isEsqlQuery) { - sendLoadingMsg(dataSubjects.totalHits$, { - result: dataSubjects.totalHits$.getValue().result, - }); - } + sendLoadingMsg(dataSubjects.totalHits$, { + result: dataSubjects.totalHits$.getValue().result, + }); // Start fetching all required requests const response = isEsqlQuery diff --git a/src/plugins/discover/public/application/main/state_management/discover_data_state_container.test.ts b/src/plugins/discover/public/application/main/state_management/discover_data_state_container.test.ts index 226a48dc1aeca..1bbb16ab3c9dd 100644 --- a/src/plugins/discover/public/application/main/state_management/discover_data_state_container.test.ts +++ b/src/plugins/discover/public/application/main/state_management/discover_data_state_container.test.ts @@ -159,7 +159,6 @@ describe('test getDataStateContainer', () => { expect( stateContainer.searchSessionManager.getCurrentSearchSessionId as jest.Mock ).toHaveBeenCalled(); - unsubscribe(); done(); } @@ -169,21 +168,24 @@ describe('test getDataStateContainer', () => { }); it('should update app state from default profile state', async () => { + mockFetchDocuments.mockResolvedValue({ records: [] }); const stateContainer = getDiscoverStateMock({ isTimeBased: true }); const dataState = stateContainer.dataState; const dataUnsub = dataState.subscribe(); const appUnsub = stateContainer.appState.initAndSync(); - discoverServiceMock.profilesManager.resolveDataSourceProfile({}); + await discoverServiceMock.profilesManager.resolveDataSourceProfile({}); stateContainer.actions.setDataView(dataViewMock); stateContainer.internalState.transitions.setResetDefaultProfileState({ columns: true, rowHeight: true, }); + dataState.data$.totalHits$.next({ fetchStatus: FetchStatus.COMPLETE, result: 0, }); dataState.refetch$.next(undefined); + await waitFor(() => { expect(dataState.data$.main$.value.fetchStatus).toBe(FetchStatus.COMPLETE); }); @@ -202,7 +204,7 @@ describe('test getDataStateContainer', () => { const dataState = stateContainer.dataState; const dataUnsub = dataState.subscribe(); const appUnsub = stateContainer.appState.initAndSync(); - discoverServiceMock.profilesManager.resolveDataSourceProfile({}); + await discoverServiceMock.profilesManager.resolveDataSourceProfile({}); stateContainer.actions.setDataView(dataViewMock); stateContainer.internalState.transitions.setResetDefaultProfileState({ columns: false, diff --git a/src/plugins/discover/public/application/main/state_management/discover_data_state_container.ts b/src/plugins/discover/public/application/main/state_management/discover_data_state_container.ts index ce7f820aa3cdd..4d01c2b72d327 100644 --- a/src/plugins/discover/public/application/main/state_management/discover_data_state_container.ts +++ b/src/plugins/discover/public/application/main/state_management/discover_data_state_container.ts @@ -185,6 +185,12 @@ export function getDataStateContainer({ documents$: new BehaviorSubject(initialState), totalHits$: new BehaviorSubject(initialState), }; + // This is debugging code, helping you to understand which messages are sent to the data observables + // Adding a debugger in the functions can be helpful to understand what triggers a message + // dataSubjects.main$.subscribe((msg) => addLog('dataSubjects.main$', msg)); + // dataSubjects.documents$.subscribe((msg) => addLog('dataSubjects.documents$', msg)); + // dataSubjects.totalHits$.subscribe((msg) => addLog('dataSubjects.totalHits$', msg);); + // Add window.ELASTIC_DISCOVER_LOGGER = 'debug' to see messages in console let autoRefreshDone: AutoRefreshDoneFn | undefined | null = null; /** diff --git a/src/plugins/discover/public/components/data_types/logs/service_name_cell.test.tsx b/src/plugins/discover/public/components/data_types/logs/service_name_cell.test.tsx index 8cf45be4f09e5..3171c5e61e629 100644 --- a/src/plugins/discover/public/components/data_types/logs/service_name_cell.test.tsx +++ b/src/plugins/discover/public/components/data_types/logs/service_name_cell.test.tsx @@ -7,15 +7,46 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import React from 'react'; import { buildDataTableRecord, DataTableRecord } from '@kbn/discover-utils'; import { dataViewMock } from '@kbn/discover-utils/src/__mocks__'; import { fieldFormatsMock } from '@kbn/field-formats-plugin/common/mocks'; import { render, screen } from '@testing-library/react'; -import React from 'react'; +import { DataGridDensity, ROWS_HEIGHT_OPTIONS } from '@kbn/unified-data-table'; import { getServiceNameCell } from './service_name_cell'; +import { CellRenderersExtensionParams } from '../../../context_awareness'; + +const core = { + application: { + capabilities: { + apm: { + show: true, + }, + }, + }, + uiSettings: { + get: () => true, + }, +}; + +jest.mock('../../../hooks/use_discover_services', () => { + const originalModule = jest.requireActual('../../../hooks/use_discover_services'); + return { + ...originalModule, + useDiscoverServices: () => ({ core, share: {} }), + }; +}); const renderCell = (serviceNameField: string, record: DataTableRecord) => { - const ServiceNameCell = getServiceNameCell(serviceNameField); + const cellRenderersExtensionParamsMock: CellRenderersExtensionParams = { + actions: { + addFilter: jest.fn(), + }, + dataView: dataViewMock, + density: DataGridDensity.COMPACT, + rowHeight: ROWS_HEIGHT_OPTIONS.single, + }; + const ServiceNameCell = getServiceNameCell(serviceNameField, cellRenderersExtensionParamsMock); render( { dataViewMock ); renderCell('service.name', record); - expect(screen.getByTestId('serviceNameCell-nodejs')).toBeInTheDocument(); - }); - - it('renders default icon with unknwon test subject if agent name is missing', () => { - const record = buildDataTableRecord( - { fields: { 'service.name': 'test-service' } }, - dataViewMock - ); - renderCell('service.name', record); - expect(screen.getByTestId('serviceNameCell-unknown')).toBeInTheDocument(); + expect(screen.getByTestId('dataTableCellActionsPopover_service.name')).toBeInTheDocument(); }); - it('does not render if service name is missing', () => { + it('does render empty div if service name is missing', () => { const record = buildDataTableRecord({ fields: { 'agent.name': 'nodejs' } }, dataViewMock); renderCell('service.name', record); - expect(screen.queryByTestId('serviceNameCell-nodejs')).not.toBeInTheDocument(); - expect(screen.queryByTestId('serviceNameCell-unknown')).not.toBeInTheDocument(); + expect(screen.queryByTestId('serviceNameCell-empty')).toBeInTheDocument(); }); }); diff --git a/src/plugins/discover/public/components/data_types/logs/service_name_cell.tsx b/src/plugins/discover/public/components/data_types/logs/service_name_cell.tsx index 39d112de5258e..cd94cd609dc69 100644 --- a/src/plugins/discover/public/components/data_types/logs/service_name_cell.tsx +++ b/src/plugins/discover/public/components/data_types/logs/service_name_cell.tsx @@ -7,19 +7,27 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; +import React from 'react'; +import { EuiToolTip } from '@elastic/eui'; import type { AgentName } from '@kbn/elastic-agent-utils'; import { dynamic } from '@kbn/shared-ux-utility'; import type { DataGridCellValueElementProps } from '@kbn/unified-data-table'; -import React from 'react'; +import { css } from '@emotion/react'; import { getFieldValue } from '@kbn/discover-utils'; +import { euiThemeVars } from '@kbn/ui-theme'; +import { CellRenderersExtensionParams } from '../../../context_awareness'; import { AGENT_NAME_FIELD } from '../../../../common/data_types/logs/constants'; +import { ServiceNameBadgeWithActions } from './service_name_badge_with_actions'; -const dataTestSubj = 'serviceNameCell'; const AgentIcon = dynamic(() => import('@kbn/custom-icons/src/components/agent_icon')); +const dataTestSubj = 'serviceNameCell'; +const agentIconStyle = css` + margin-right: ${euiThemeVars.euiSizeXS}; +`; export const getServiceNameCell = - (serviceNameField: string) => (props: DataGridCellValueElementProps) => { + (serviceNameField: string, { actions }: CellRenderersExtensionParams) => + (props: DataGridCellValueElementProps) => { const serviceNameValue = getFieldValue(props.row, serviceNameField) as string; const agentName = getFieldValue(props.row, AGENT_NAME_FIELD) as AgentName; @@ -27,19 +35,18 @@ export const getServiceNameCell = return -; } + const getIcon = () => ( + + + + ); + return ( - - - - - - - {serviceNameValue} - + ); }; diff --git a/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/accessors/get_cell_renderers.tsx b/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/accessors/get_cell_renderers.tsx index 9e45892070120..7e13baf8ddcf9 100644 --- a/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/accessors/get_cell_renderers.tsx +++ b/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/accessors/get_cell_renderers.tsx @@ -31,8 +31,8 @@ export const getCellRenderers: DataSourceProfileProvider['profile']['getCellRend ...SERVICE_NAME_FIELDS.reduce( (acc, field) => ({ ...acc, - [field]: getServiceNameCell(field), - [`${field}.keyword`]: getServiceNameCell(`${field}.keyword`), + [field]: getServiceNameCell(field, params), + [`${field}.keyword`]: getServiceNameCell(`${field}.keyword`, params), }), {} ), diff --git a/src/plugins/kibana_usage_collection/server/collectors/common/counters.test.ts b/src/plugins/kibana_usage_collection/server/collectors/common/counters.test.ts index 6a64e6b597e6a..33e9b68f59c4a 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/common/counters.test.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/common/counters.test.ts @@ -118,7 +118,6 @@ describe('createCounterFetcher', () => { dailyEvents, }) ); - // @ts-expect-error incomplete mock implementation const { dailyEvents } = await fetch({ soClient: soClientMock }); expect(dailyEvents).toHaveLength(5); const intersectingEntry = dailyEvents.find( diff --git a/src/plugins/kibana_usage_collection/server/collectors/common/counters.ts b/src/plugins/kibana_usage_collection/server/collectors/common/counters.ts index 9300ddcb959aa..df0ca67a85198 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/common/counters.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/common/counters.ts @@ -30,7 +30,7 @@ export function createCounterFetcher( filter: string, transform: (counters: CounterEvent[]) => T ) { - return async ({ soClient }: CollectorFetchContext) => { + return async ({ soClient }: Pick) => { const finder = soClient.createPointInTimeFinder({ type: USAGE_COUNTERS_SAVED_OBJECT_TYPE, namespaces: ['*'], diff --git a/src/plugins/kibana_usage_collection/server/collectors/core/fetch_deprecated_api_counters.ts b/src/plugins/kibana_usage_collection/server/collectors/core/fetch_deprecated_api_counters.ts new file mode 100644 index 0000000000000..1c90b23e40b16 --- /dev/null +++ b/src/plugins/kibana_usage_collection/server/collectors/core/fetch_deprecated_api_counters.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import type { Logger } from '@kbn/logging'; +import type { CoreDeprecatedApiUsageStats } from '@kbn/core-usage-data-server'; +import { USAGE_COUNTERS_SAVED_OBJECT_TYPE } from '@kbn/usage-collection-plugin/server'; + +import { createCounterFetcher, type CounterEvent } from '../common/counters'; + +const DEPRECATED_API_COUNTERS_FILTER = `${USAGE_COUNTERS_SAVED_OBJECT_TYPE}.attributes.counterType: deprecated_api_call\\:*`; + +const mergeCounter = (counter: CounterEvent, acc?: CoreDeprecatedApiUsageStats) => { + if (acc && acc?.apiId !== counter.counterName) { + throw new Error( + `Failed to merge mismatching counterNames: ${acc.apiId} with ${counter.counterName}` + ); + } + const isMarkedCounter = counter.counterType.endsWith(':marked_as_resolved'); + + const finalCounter = { + apiId: counter.counterName, + apiTotalCalls: 0, + apiLastCalledAt: 'unknown', + totalMarkedAsResolved: 0, + markedAsResolvedLastCalledAt: 'unknown', + ...(acc || {}), + }; + + if (isMarkedCounter) { + return finalCounter; + } + + const isResolvedCounter = counter.counterType.endsWith(':resolved'); + const totalKey = isResolvedCounter ? 'totalMarkedAsResolved' : 'apiTotalCalls'; + const lastUpdatedKey = isResolvedCounter ? 'markedAsResolvedLastCalledAt' : 'apiLastCalledAt'; + + const newPayload = { + [totalKey]: (finalCounter[totalKey] || 0) + counter.total, + [lastUpdatedKey]: counter.lastUpdatedAt, + }; + + return { + ...finalCounter, + ...newPayload, + }; +}; + +function mergeCounters(counters: CounterEvent[]): CoreDeprecatedApiUsageStats[] { + const mergedCounters = counters.reduce((acc, counter) => { + const { counterName } = counter; + const existingCounter = acc[counterName]; + + acc[counterName] = mergeCounter(counter, existingCounter); + + return acc; + }, {} as Record); + + return Object.values(mergedCounters); +} + +export const fetchDeprecatedApiCounterStats = (logger: Logger) => { + return createCounterFetcher(logger, DEPRECATED_API_COUNTERS_FILTER, mergeCounters); +}; diff --git a/src/plugins/kibana_usage_collection/server/collectors/core/index.ts b/src/plugins/kibana_usage_collection/server/collectors/core/index.ts index 0e0f783b0f847..e298560893ccb 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/core/index.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/core/index.ts @@ -8,3 +8,4 @@ */ export { registerCoreUsageCollector } from './core_usage_collector'; +export { fetchDeprecatedApiCounterStats } from './fetch_deprecated_api_counters'; diff --git a/src/plugins/kibana_usage_collection/server/collectors/index.ts b/src/plugins/kibana_usage_collection/server/collectors/index.ts index ef5287324ee59..c22fb3b5697f8 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/index.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/index.ts @@ -17,7 +17,7 @@ export { export { registerOpsStatsCollector } from './ops_stats'; export { registerCloudProviderUsageCollector } from './cloud'; export { registerCspCollector } from './csp'; -export { registerCoreUsageCollector } from './core'; +export { registerCoreUsageCollector, fetchDeprecatedApiCounterStats } from './core'; export { registerLocalizationUsageCollector } from './localization'; export { registerConfigUsageCollector } from './config_usage'; export { registerUiCountersUsageCollector } from './ui_counters'; diff --git a/src/plugins/kibana_usage_collection/server/plugin.ts b/src/plugins/kibana_usage_collection/server/plugin.ts index bbfc010c0e065..48fb1c6ff7b9b 100644 --- a/src/plugins/kibana_usage_collection/server/plugin.ts +++ b/src/plugins/kibana_usage_collection/server/plugin.ts @@ -43,6 +43,7 @@ import { registerUsageCountersUsageCollector, registerSavedObjectsCountUsageCollector, registerEventLoopDelaysCollector, + fetchDeprecatedApiCounterStats, } from './collectors'; interface KibanaUsageCollectionPluginsDepsSetup { @@ -74,6 +75,10 @@ export class KibanaUsageCollectionPlugin implements Plugin { registerEbtCounters(coreSetup.analytics, usageCollection); this.eventLoopUsageCounter = usageCollection.createUsageCounter('eventLoop'); coreSetup.coreUsageData.registerUsageCounter(usageCollection.createUsageCounter('core')); + const deprecatedUsageFetch = fetchDeprecatedApiCounterStats( + this.logger.get('deprecated-api-usage') + ); + coreSetup.coreUsageData.registerDeprecatedUsageFetch(deprecatedUsageFetch); this.registerUsageCollectors( usageCollection, coreSetup, diff --git a/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx b/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx index 0d42b7c8f241a..6a3dabd9a0ceb 100644 --- a/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx +++ b/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx @@ -10,14 +10,15 @@ import React, { ReactElement } from 'react'; import classNames from 'classnames'; -import { MountPoint } from '@kbn/core/public'; +import type { MountPoint } from '@kbn/core/public'; import { MountPointPortal } from '@kbn/react-kibana-mount'; -import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; -import { StatefulSearchBarProps } from '@kbn/unified-search-plugin/public'; -import { AggregateQuery, Query } from '@kbn/es-query'; -import { TopNavMenuData } from './top_nav_menu_data'; +import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; +import type { StatefulSearchBarProps } from '@kbn/unified-search-plugin/public'; +import type { AggregateQuery, Query } from '@kbn/es-query'; +import type { EuiBreakpointSize } from '@elastic/eui'; +import type { TopNavMenuData } from './top_nav_menu_data'; import { TopNavMenuItems } from './top_nav_menu_items'; -import { TopNavMenuBadgeProps, TopNavMenuBadges } from './top_nav_menu_badges'; +import { type TopNavMenuBadgeProps, TopNavMenuBadges } from './top_nav_menu_badges'; export type TopNavMenuProps = Omit< StatefulSearchBarProps, @@ -51,6 +52,11 @@ export type TopNavMenuProps = Omit< * ``` */ setMenuMountPoint?: (menuMount: MountPoint | undefined) => void; + + /** + * A list of named breakpoints at which to show the popover version. If not provided, it will use the default set of ['xs', 's'] that is internally provided by EUI. + */ + popoverBreakpoints?: EuiBreakpointSize[]; }; /* @@ -76,7 +82,13 @@ export function TopNavMenu( } function renderMenu(className: string): ReactElement | null { - return ; + return ( + + ); } function renderSearchBar(): ReactElement | null { diff --git a/src/plugins/navigation/public/top_nav_menu/top_nav_menu_items.tsx b/src/plugins/navigation/public/top_nav_menu/top_nav_menu_items.tsx index 48179f30ec276..e8d118dadff7d 100644 --- a/src/plugins/navigation/public/top_nav_menu/top_nav_menu_items.tsx +++ b/src/plugins/navigation/public/top_nav_menu/top_nav_menu_items.tsx @@ -7,21 +7,30 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { EuiHeaderLinks } from '@elastic/eui'; +import { EuiBreakpointSize, EuiHeaderLinks } from '@elastic/eui'; import React from 'react'; import type { TopNavMenuData } from './top_nav_menu_data'; import { TopNavMenuItem } from './top_nav_menu_item'; +interface TopNavMenuItemsProps { + config: TopNavMenuData[] | undefined; + className?: string; + popoverBreakpoints?: EuiBreakpointSize[]; +} + export const TopNavMenuItems = ({ config, className, -}: { - config: TopNavMenuData[] | undefined; - className?: string; -}) => { + popoverBreakpoints, +}: TopNavMenuItemsProps) => { if (!config || config.length === 0) return null; return ( - + {config.map((menuItem: TopNavMenuData, i: number) => { return ; })} diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.test.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.test.tsx index 9297b84de6dbc..9b95e58baac38 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.test.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.test.tsx @@ -415,6 +415,21 @@ describe('SharingMetaFields', () => { `); }); + it('Should convert to absolute correctly', () => { + jest.useFakeTimers().setSystemTime(new Date('2024-10-21T10:19:31.254Z')); + + const from = 'now-1d/d'; + const to = 'now-1d/d'; + const component = ; + + expect(shallow(component)).toMatchInlineSnapshot(` +
+ `); + }); + it('Should render the component without data-shared-timefilter-duration if time is not set correctly', () => { const component = ( diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx index 8321ca9130f54..4d4db4438d74d 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx @@ -209,7 +209,7 @@ export const SharingMetaFields = React.memo(function SharingMetaFields({ try { const dateRangePretty = usePrettyDuration({ timeFrom: toAbsoluteString(from), - timeTo: toAbsoluteString(to), + timeTo: toAbsoluteString(to, true), quickRanges: [], dateFormat, }); diff --git a/src/plugins/vis_type_markdown/public/markdown_vis.ts b/src/plugins/vis_type_markdown/public/markdown_vis.ts index 095dc4dee6b89..10bcf8457cab2 100644 --- a/src/plugins/vis_type_markdown/public/markdown_vis.ts +++ b/src/plugins/vis_type_markdown/public/markdown_vis.ts @@ -23,10 +23,10 @@ export const markdownVisDefinition: VisTypeDefinition = { icon: 'visText', group: VisGroups.TOOLS, titleInWizard: i18n.translate('visTypeMarkdown.markdownTitleInWizard', { - defaultMessage: 'Text', + defaultMessage: 'Markdown text', }), description: i18n.translate('visTypeMarkdown.markdownDescription', { - defaultMessage: 'Add text and images to your dashboard.', + defaultMessage: 'Add custom text or images to dashboards.', }), order: 30, toExpressionAst, diff --git a/src/plugins/vis_types/timeseries/public/metrics_type.ts b/src/plugins/vis_types/timeseries/public/metrics_type.ts index 0790694a24473..eec28bb6cf47c 100644 --- a/src/plugins/vis_types/timeseries/public/metrics_type.ts +++ b/src/plugins/vis_types/timeseries/public/metrics_type.ts @@ -102,7 +102,7 @@ export const metricsVisDefinition: VisTypeDefinition< name: VIS_TYPE, title: i18n.translate('visTypeTimeseries.kbnVisTypes.metricsTitle', { defaultMessage: 'TSVB' }), description: i18n.translate('visTypeTimeseries.kbnVisTypes.metricsDescription', { - defaultMessage: 'Perform advanced analysis of your time series data.', + defaultMessage: 'Create visualizations using time series data.', }), icon: 'visVisualBuilder', group: VisGroups.LEGACY, diff --git a/src/plugins/vis_types/vega/public/vega_type.ts b/src/plugins/vis_types/vega/public/vega_type.ts index a59358d024c75..744125fe5be2c 100644 --- a/src/plugins/vis_types/vega/public/vega_type.ts +++ b/src/plugins/vis_types/vega/public/vega_type.ts @@ -32,12 +32,9 @@ export const createVegaTypeDefinition = (): VisTypeDefinition => { title: 'Vega', getInfoMessage, description: i18n.translate('visTypeVega.type.vegaDescription', { - defaultMessage: 'Use Vega to create new types of visualizations.', + defaultMessage: 'Use the Vega syntax to create new types of visualizations.', description: 'Vega and Vega-Lite are product names and should not be translated', }), - note: i18n.translate('visTypeVega.type.vegaNote', { - defaultMessage: 'Requires knowledge of Vega syntax.', - }), icon: 'visVega', group: VisGroups.PROMOTED, titleInWizard: i18n.translate('visTypeVega.type.vegaTitleInWizard', { diff --git a/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.test.tsx b/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.test.tsx index 286ceecaca567..16da39c059bba 100644 --- a/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.test.tsx +++ b/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.test.tsx @@ -70,18 +70,18 @@ describe('AggBasedSelection', () => { jest.clearAllMocks(); }); - it('should call the toggleGroups if the user clicks the goBack link', () => { - const toggleGroups = jest.fn(); + it('should call the showMainDialog if the user clicks the goBack link', () => { + const showMainDialog = jest.fn(); const wrapper = mountWithIntl( ); const aggBasedGroupCard = wrapper.find('[data-test-subj="goBackLink"]').last(); aggBasedGroupCard.simulate('click'); - expect(toggleGroups).toHaveBeenCalled(); + expect(showMainDialog).toHaveBeenCalled(); }); describe('filter for visualization types', () => { @@ -89,7 +89,7 @@ describe('AggBasedSelection', () => { const wrapper = mountWithIntl( ); diff --git a/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.tsx b/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.tsx index f4a2768372125..1cb038e338f0e 100644 --- a/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.tsx +++ b/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.tsx @@ -39,7 +39,7 @@ interface AggBasedSelectionProps { openedAsRoot?: boolean; onVisTypeSelected: (visType: BaseVisType) => void; visTypesRegistry: TypesStart; - toggleGroups: (flag: boolean) => void; + showMainDialog: (flag: boolean) => void; } interface AggBasedSelectionState { query: string; @@ -67,7 +67,7 @@ class AggBasedSelection extends React.Component {this.props.openedAsRoot ? null : ( - this.props.toggleGroups(true)} /> + this.props.showMainDialog(true)} /> )} { const defaultVisTypeParams = { @@ -81,11 +83,11 @@ describe('GroupSelection', () => { const docLinks = { links: { - dashboard: { + visualize: { guide: 'test', }, }, - }; + } as unknown as DocLinksStart; beforeAll(() => { Object.defineProperty(window, 'location', { @@ -99,35 +101,34 @@ describe('GroupSelection', () => { jest.clearAllMocks(); }); - it('should render the header title', () => { - const wrapper = mountWithIntl( - - ); - expect(wrapper.find('[data-test-subj="groupModalHeader"]').at(0).text()).toBe( - 'New visualization' + const renderGroupSelectionComponent = (overrideProps?: Partial) => { + return render( + + + ); + }; + + it('should render the header title', () => { + renderGroupSelectionComponent(); + expect(screen.getByTestId('groupModalHeader')).toHaveTextContent('Create visualization'); }); - it('should not render the aggBased group card if no aggBased visualization is registered', () => { - const wrapper = mountWithIntl( - - ); - expect(wrapper.find('[data-test-subj="visGroup-aggbased"]').exists()).toBe(false); + it('should not render tabs if no legacy, tools or tsvb visualizations are registered', async () => { + renderGroupSelectionComponent(); + expect(screen.queryByRole('tab', { name: /legacy/i })).toBeNull(); + expect(screen.queryByRole('tab', { name: /recommended/i })).toBeNull(); }); - it('should render the aggBased group card if an aggBased group vis is registered', () => { + it('should render tabs and the aggBased group card if an aggBased group vis is registered', async () => { const aggBasedVisType = { name: 'visWithSearch', title: 'Vis with search', @@ -135,53 +136,18 @@ describe('GroupSelection', () => { stage: 'production', ...defaultVisTypeParams, }; - const wrapper = mountWithIntl( - - ); - expect(wrapper.find('[data-test-subj="visGroup-aggbased"]').exists()).toBe(true); - }); + renderGroupSelectionComponent({ + visTypesRegistry: visTypesRegistry([..._visTypes, aggBasedVisType] as BaseVisType[]), + tab: 'legacy', + }); - it('should not render the tools group card if no tools visualization is registered', () => { - const wrapper = mountWithIntl( - - ); - expect(wrapper.find('[data-test-subj="visGroup-tools"]').exists()).toBe(false); + expect(screen.queryByRole('tab', { name: /legacy/i })).toBeInTheDocument(); + expect(screen.queryByRole('tab', { name: /recommended/i })).toBeInTheDocument(); + expect(screen.getByTestId('visType-aggbased')).toHaveTextContent('Aggregation-based'); }); - it('should render the tools group card if a tools group vis is registered', () => { - const toolsVisType = { - name: 'vis3', - title: 'Vis3', - stage: 'production', - group: VisGroups.TOOLS, - ...defaultVisTypeParams, - }; - const wrapper = mountWithIntl( - - ); - expect(wrapper.find('[data-test-subj="visGroup-tools"]').exists()).toBe(true); - }); - - it('should call the toggleGroups if the aggBased group card is clicked', () => { - const toggleGroups = jest.fn(); + it('should call the showMainDialog if the aggBased group card is clicked', async () => { + const showMainDialog = jest.fn(); const aggBasedVisType = { name: 'visWithSearch', title: 'Vis with search', @@ -189,82 +155,26 @@ describe('GroupSelection', () => { stage: 'production', ...defaultVisTypeParams, }; - const wrapper = mountWithIntl( - - ); - const aggBasedGroupCard = wrapper.find('[data-test-subj="visGroupAggBasedExploreLink"]').last(); - aggBasedGroupCard.simulate('click'); - expect(toggleGroups).toHaveBeenCalled(); + renderGroupSelectionComponent({ + showMainDialog, + visTypesRegistry: visTypesRegistry([..._visTypes, aggBasedVisType] as BaseVisType[]), + tab: 'legacy', + }); + + await userEvent.click(screen.getByRole('button', { name: /Aggregation-based/i })); + expect(showMainDialog).toHaveBeenCalledWith(false); }); - it('should sort promoted visualizations first', () => { - const wrapper = mountWithIntl( - - ); + it('should only show promoted visualizations in recommended tab', () => { + renderGroupSelectionComponent(); - const cards = [ - ...new Set( - wrapper.find('[data-test-subj^="visType-"]').map((card) => card.prop('data-test-subj')) - ), - ]; + const cards = screen.getAllByRole('button').map((el) => el.textContent); expect(cards).toEqual([ - 'visType-visAliasWithPromotion', - 'visType-vis1', - 'visType-vis2', - 'visType-visWithAliasUrl', + 'Vis alias with promotion', + 'Vis Type 1', + 'Vis Type 2', + 'Vis with alias Url', ]); }); - - it('should not show tools experimental visualizations if showExperimentalis false', () => { - const expVis = { - name: 'visExp', - title: 'Experimental Vis', - group: VisGroups.TOOLS, - stage: 'experimental', - ...defaultVisTypeParams, - }; - const wrapper = mountWithIntl( - - ); - expect(wrapper.find('[data-test-subj="visType-visExp"]').exists()).toBe(false); - }); - - it('should show tools experimental visualizations if showExperimental is true', () => { - const expVis = { - name: 'visExp', - title: 'Experimental Vis', - group: VisGroups.TOOLS, - stage: 'experimental', - ...defaultVisTypeParams, - }; - const wrapper = mountWithIntl( - - ); - expect(wrapper.find('[data-test-subj="visType-visExp"]').exists()).toBe(true); - }); }); diff --git a/src/plugins/visualizations/public/wizard/group_selection/group_selection.tsx b/src/plugins/visualizations/public/wizard/group_selection/group_selection.tsx index 11f85c0c685a3..32a59fd56c1e5 100644 --- a/src/plugins/visualizations/public/wizard/group_selection/group_selection.tsx +++ b/src/plugins/visualizations/public/wizard/group_selection/group_selection.tsx @@ -8,7 +8,7 @@ */ import { FormattedMessage } from '@kbn/i18n-react'; -import React, { useCallback, useMemo } from 'react'; +import React, { ReactNode, useCallback, useMemo } from 'react'; import { orderBy } from 'lodash'; import { EuiFlexGroup, @@ -20,14 +20,13 @@ import { EuiModalBody, EuiModalHeaderTitle, EuiLink, - EuiText, EuiSpacer, - EuiBetaBadge, - EuiTitle, EuiDescriptionListTitle, EuiDescriptionListDescription, EuiDescriptionList, - EuiBadge, + EuiTabs, + EuiTab, + EuiIconTip, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { DocLinksStart } from '@kbn/core/public'; @@ -36,162 +35,210 @@ import { VisGroups } from '../../vis_types/vis_groups_enum'; import type { VisTypeAlias } from '../../vis_types/vis_type_alias_registry'; import './group_selection.scss'; -interface GroupSelectionProps { +export interface GroupSelectionProps { onVisTypeSelected: (visType: BaseVisType | VisTypeAlias) => void; visTypesRegistry: TypesStart; docLinks: DocLinksStart; - toggleGroups: (flag: boolean) => void; - showExperimental: boolean; + showMainDialog: (flag: boolean) => void; + tab: 'recommended' | 'legacy'; + setTab: (tab: 'recommended' | 'legacy') => void; } interface VisCardProps { onVisTypeSelected: (visType: BaseVisType | VisTypeAlias) => void; visType: BaseVisType | VisTypeAlias; - showExperimental?: boolean | undefined; + shouldStretch?: boolean; } -function GroupSelection(props: GroupSelectionProps) { - const visualizeGuideLink = props.docLinks.links.dashboard.guide; +const tabs: Array<{ id: 'recommended' | 'legacy'; label: ReactNode; dataTestSubj: string }> = [ + { + id: 'recommended', + label: i18n.translate('visualizations.newVisWizard.recommendedTab', { + defaultMessage: 'Recommended', + }), + dataTestSubj: 'groupModalRecommendedTab', + }, + { + id: 'legacy', + dataTestSubj: 'groupModalLegacyTab', + label: ( + + + {i18n.translate('visualizations.newVisWizard.legacyTab', { + defaultMessage: 'Legacy', + })} + + + + + + + ), + }, +]; + +const getVisTypesFromGroup = ( + visTypesRegistry: TypesStart, + group: VisGroups +): Array => { + return visTypesRegistry.getByGroup(group).filter(({ disableCreate }) => !disableCreate); +}; + +function GroupSelection({ + tab = 'recommended', + setTab, + visTypesRegistry, + ...props +}: GroupSelectionProps) { + const visualizeGuideLink = props.docLinks.links.visualize.guide; const promotedVisGroups = useMemo( () => orderBy( [ - ...props.visTypesRegistry.getAliases(), - ...props.visTypesRegistry.getByGroup(VisGroups.PROMOTED), - // Include so TSVB still gets displayed - ...props.visTypesRegistry.getByGroup(VisGroups.LEGACY), + ...visTypesRegistry.getAliases(), + ...visTypesRegistry.getByGroup(VisGroups.PROMOTED), ].filter((visDefinition) => { return !visDefinition.disableCreate; }), ['promotion', 'title'], ['asc', 'asc'] ), - [props.visTypesRegistry] + [visTypesRegistry] ); + const aggBasedTypes = getVisTypesFromGroup(visTypesRegistry, VisGroups.AGGBASED); + const legacyTypes = getVisTypesFromGroup(visTypesRegistry, VisGroups.LEGACY); + + const shouldDisplayLegacyTab = legacyTypes.length + aggBasedTypes.length; + + const [tsvbProps] = legacyTypes.map((visType) => ({ + visType: { + ...visType, + icon: visType.name === 'metrics' ? 'visualizeApp' : (visType.icon as string), + }, + onVisTypeSelected: props.onVisTypeSelected, + key: visType.name, + })); + return ( <> + {shouldDisplayLegacyTab && ( +
+ + {tabs.map((t) => ( + setTab(t.id)} + key={t.id} + > + {t.label} + + ))} + +
+ )} +
- - {promotedVisGroups.map((visType) => ( - - ))} - - -
-
- - - {props.visTypesRegistry.getByGroup(VisGroups.AGGBASED).filter((visDefinition) => { - return !visDefinition.disableCreate; - }).length > 0 && ( - - props.toggleGroups(false)} - title={ - - {i18n.translate('visualizations.newVisWizard.aggBasedGroupTitle', { - defaultMessage: 'Aggregation based', - })} - - } - data-test-subj="visGroup-aggbased" - description={i18n.translate( - 'visualizations.newVisWizard.aggBasedGroupDescription', - { - defaultMessage: - 'Use our classic visualize library to create charts based on aggregations.', - } - )} - icon={} - > - props.toggleGroups(false)} - > - - {i18n.translate('visualizations.newVisWizard.exploreOptionLinkText', { - defaultMessage: 'Explore options', - })}{' '} - - - - - - )} - {props.visTypesRegistry.getByGroup(VisGroups.TOOLS).length > 0 && ( - - - - - {i18n.translate('visualizations.newVisWizard.toolsGroupTitle', { - defaultMessage: 'Tools', - })} - - - -
- {props.visTypesRegistry.getByGroup(VisGroups.TOOLS).map((visType) => ( - - ))} -
-
- )} -
- - - - - - - - + {promotedVisGroups.map((visType) => ( + - - - + ))} + + ) : ( + + {tsvbProps ? : null} + { + { + props.showMainDialog(false); + }} + /> + } + + )} + +
+ +
); } -const VisGroup = ({ visType, onVisTypeSelected }: VisCardProps) => { +const ModalFooter = ({ visualizeGuideLink }: { visualizeGuideLink: string }) => { + return ( +
+ + + + + + + + + + + +
+ ); +}; + +const VisGroup = ({ visType, onVisTypeSelected, shouldStretch = false }: VisCardProps) => { const onClick = useCallback(() => { onVisTypeSelected(visType); }, [onVisTypeSelected, visType]); return ( - + { } layout="horizontal" - icon={} + icon={} /> ); }; -const ToolsGroup = ({ visType, onVisTypeSelected, showExperimental }: VisCardProps) => { - const onClick = useCallback(() => { - onVisTypeSelected(visType); - }, [onVisTypeSelected, visType]); - // hide both the hidden visualizations and, if lab mode is not enabled, the experimental visualizations - // TODO: Remove the showExperimental logic as part of https://github.com/elastic/kibana/issues/152833 - if (visType.disableCreate || (!showExperimental && visType.stage === 'experimental')) { - return null; - } - return ( - - - - - - - - - {'titleInWizard' in visType && visType.titleInWizard - ? visType.titleInWizard - : visType.title} - - - {visType.stage === 'experimental' && !visType.isDeprecated ? ( - - - - ) : ( - visType.isDeprecated && ( - - - - - - ) - )} - - - {visType.description} - - - - ); -}; - export { GroupSelection }; diff --git a/src/plugins/visualizations/public/wizard/new_vis_modal.test.tsx b/src/plugins/visualizations/public/wizard/new_vis_modal.test.tsx index 0451d606060da..7f73addb93116 100644 --- a/src/plugins/visualizations/public/wizard/new_vis_modal.test.tsx +++ b/src/plugins/visualizations/public/wizard/new_vis_modal.test.tsx @@ -8,13 +8,15 @@ */ import React from 'react'; -import { mountWithIntl } from '@kbn/test-jest-helpers'; import { TypesStart, VisGroups, BaseVisType } from '../vis_types'; -import NewVisModal from './new_vis_modal'; +import NewVisModal, { TypeSelectionProps } from './new_vis_modal'; import { ApplicationStart, DocLinksStart } from '@kbn/core/public'; import { embeddablePluginMock } from '@kbn/embeddable-plugin/public/mocks'; import { contentManagementMock } from '@kbn/content-management-plugin/public/mocks'; import { VisParams } from '../../common'; +import { render, screen } from '@testing-library/react'; +import { I18nProvider } from '@kbn/i18n-react'; +import userEvent from '@testing-library/user-event'; describe('NewVisModal', () => { const defaultVisTypeParams = { @@ -76,11 +78,11 @@ describe('NewVisModal', () => { const uiSettings: any = { get: settingsGet }; const docLinks = { links: { - dashboard: { + visualize: { guide: 'test', }, }, - }; + } as unknown as DocLinksStart; const contentManagement = contentManagementMock.createStartContract(); @@ -96,58 +98,9 @@ describe('NewVisModal', () => { jest.clearAllMocks(); }); - it('should show the aggbased group but not the visualization assigned to this group', () => { - const wrapper = mountWithIntl( - null} - visTypesRegistry={visTypes} - addBasePath={addBasePath} - uiSettings={uiSettings} - application={{} as ApplicationStart} - docLinks={docLinks as DocLinksStart} - contentClient={contentManagement.client} - /> - ); - expect(wrapper.find('[data-test-subj="visGroup-aggbased"]').exists()).toBe(true); - expect(wrapper.find('[data-test-subj="visType-visWithSearch"]').exists()).toBe(false); - }); - - it('should show the tools group', () => { - const wrapper = mountWithIntl( - null} - visTypesRegistry={visTypes} - addBasePath={addBasePath} - uiSettings={uiSettings} - application={{} as ApplicationStart} - docLinks={docLinks as DocLinksStart} - contentClient={contentManagement.client} - /> - ); - expect(wrapper.find('[data-test-subj="visGroup-tools"]').exists()).toBe(true); - }); - - it('should display the visualizations of the other group', () => { - const wrapper = mountWithIntl( - null} - visTypesRegistry={visTypes} - addBasePath={addBasePath} - uiSettings={uiSettings} - application={{} as ApplicationStart} - docLinks={docLinks as DocLinksStart} - contentClient={contentManagement.client} - /> - ); - expect(wrapper.find('[data-test-subj="visType-vis2"]').exists()).toBe(true); - }); - - describe('open editor', () => { - it('should open the editor for visualizations without search', () => { - const wrapper = mountWithIntl( + const renderNewVisModal = (propsOverrides?: Partial) => { + return render( + null} @@ -155,57 +108,57 @@ describe('NewVisModal', () => { addBasePath={addBasePath} uiSettings={uiSettings} application={{} as ApplicationStart} - docLinks={docLinks as DocLinksStart} + docLinks={docLinks} contentClient={contentManagement.client} + {...propsOverrides} /> - ); - const visCard = wrapper.find('[data-test-subj="visType-vis"]').last(); - visCard.simulate('click'); + + ); + }; + + it('should show the aggbased group but not the visualization assigned to this group', async () => { + renderNewVisModal(); + expect(screen.queryByText('Aggregation-based')).not.toBeInTheDocument(); + expect(screen.queryByText('Vis with search')).not.toBeInTheDocument(); + await userEvent.click(screen.getByRole('tab', { name: /Legacy/i })); + expect(screen.queryByText('Aggregation-based')).toBeInTheDocument(); + expect(screen.queryByText('Vis with search')).not.toBeInTheDocument(); + }); + + it('should display the visualizations of the other group', () => { + renderNewVisModal(); + expect(screen.queryByText('Vis Type 2')).toBeInTheDocument(); + }); + + describe('open editor', () => { + it('should open the editor for visualizations without search', async () => { + renderNewVisModal(); + await userEvent.click(screen.getByText('Vis Type 1')); expect(window.location.assign).toBeCalledWith('testbasepath/app/visualize#/create?type=vis'); }); - it('passes through editor params to the editor URL', () => { - const wrapper = mountWithIntl( - null} - visTypesRegistry={visTypes} - editorParams={['foo=true', 'bar=42']} - addBasePath={addBasePath} - uiSettings={uiSettings} - application={{} as ApplicationStart} - docLinks={docLinks as DocLinksStart} - contentClient={contentManagement.client} - /> - ); - const visCard = wrapper.find('[data-test-subj="visType-vis"]').last(); - visCard.simulate('click'); + it('passes through editor params to the editor URL', async () => { + renderNewVisModal({ + editorParams: ['foo=true', 'bar=42'], + }); + await userEvent.click(screen.getByText('Vis Type 1')); expect(window.location.assign).toBeCalledWith( 'testbasepath/app/visualize#/create?type=vis&foo=true&bar=42' ); }); - it('closes and redirects properly if visualization with alias.path and originatingApp in props', () => { + it('closes and redirects properly if visualization with alias.path and originatingApp in props', async () => { const onClose = jest.fn(); const navigateToApp = jest.fn(); const stateTransfer = embeddablePluginMock.createStartContract().getStateTransfer(); - const wrapper = mountWithIntl( - - ); - const visCard = wrapper.find('[data-test-subj="visType-visWithAliasUrl"]').last(); - visCard.simulate('click'); + renderNewVisModal({ + editorParams: ['foo=true', 'bar=42'], + onClose, + application: { navigateToApp } as unknown as ApplicationStart, + originatingApp: 'coolJestTestApp', + stateTransfer, + }); + await userEvent.click(screen.getByText('Vis with alias Url')); expect(stateTransfer.navigateToEditor).toBeCalledWith('otherApp', { path: '#/aliasUrl', state: { originatingApp: 'coolJestTestApp' }, @@ -213,48 +166,28 @@ describe('NewVisModal', () => { expect(onClose).toHaveBeenCalled(); }); - it('closes and redirects properly if visualization with aliasApp and without originatingApp in props', () => { + it('closes and redirects properly if visualization with aliasApp and without originatingApp in props', async () => { const onClose = jest.fn(); const navigateToApp = jest.fn(); - const wrapper = mountWithIntl( - - ); - const visCard = wrapper.find('[data-test-subj="visType-visWithAliasUrl"]').last(); - visCard.simulate('click'); + + renderNewVisModal({ + editorParams: ['foo=true', 'bar=42'], + onClose, + application: { navigateToApp } as unknown as ApplicationStart, + }); + await userEvent.click(screen.getByText('Vis with alias Url')); expect(navigateToApp).toBeCalledWith('otherApp', { path: '#/aliasUrl' }); expect(onClose).toHaveBeenCalled(); }); }); describe('aggBased visualizations', () => { - it('should render as expected', () => { - const wrapper = mountWithIntl( - null} - visTypesRegistry={visTypes} - addBasePath={addBasePath} - uiSettings={uiSettings} - application={{} as ApplicationStart} - docLinks={docLinks as DocLinksStart} - contentClient={contentManagement.client} - /> - ); - const aggBasedGroupCard = wrapper - .find('[data-test-subj="visGroupAggBasedExploreLink"]') - .last(); - aggBasedGroupCard.simulate('click'); - expect(wrapper.find('[data-test-subj="visType-visWithSearch"]').exists()).toBe(true); + it('should render as expected', async () => { + renderNewVisModal(); + await userEvent.click(screen.getByRole('tab', { name: /Legacy/i })); + expect(screen.queryByText('Aggregation-based')).toBeInTheDocument(); + await userEvent.click(screen.getByText('Aggregation-based')); + expect(screen.queryByText('Vis with search')).toBeInTheDocument(); }); }); }); diff --git a/src/plugins/visualizations/public/wizard/new_vis_modal.tsx b/src/plugins/visualizations/public/wizard/new_vis_modal.tsx index 2b8ea96eb4300..12fa7e1a9954d 100644 --- a/src/plugins/visualizations/public/wizard/new_vis_modal.tsx +++ b/src/plugins/visualizations/public/wizard/new_vis_modal.tsx @@ -22,7 +22,7 @@ import { AggBasedSelection } from './agg_based_selection'; import type { TypesStart, BaseVisType, VisTypeAlias } from '../vis_types'; import './dialog.scss'; -interface TypeSelectionProps { +export interface TypeSelectionProps { contentClient: ContentClient; isOpen: boolean; onClose: () => void; @@ -41,8 +41,9 @@ interface TypeSelectionProps { interface TypeSelectionState { showSearchVisModal: boolean; - showGroups: boolean; + isMainDialogShown: boolean; visType?: BaseVisType; + tab: 'recommended' | 'legacy'; } // TODO: redirect logic is specific to visualise & dashboard @@ -64,11 +65,16 @@ class NewVisModal extends React.Component { + this.setState({ tab }); + }; + public render() { if (!this.props.isOpen) { return null; @@ -82,7 +88,7 @@ class NewVisModal extends React.Component this.setState({ showGroups: flag })} + setTab={this.setTab} + tab={this.state.tab} + showMainDialog={(shouldMainBeShown: boolean) => { + this.setState({ isMainDialogShown: shouldMainBeShown }); + if (shouldMainBeShown) { + this.setTab('legacy'); + } + }} openedAsRoot={this.props.showAggsSelection && !this.props.selectedVisType} /> diff --git a/test/api_integration/apis/custom_integration/integrations.ts b/test/api_integration/apis/custom_integration/integrations.ts index 26a8777e23800..e04a119aa06ea 100644 --- a/test/api_integration/apis/custom_integration/integrations.ts +++ b/test/api_integration/apis/custom_integration/integrations.ts @@ -25,7 +25,7 @@ export default function ({ getService }: FtrProviderContext) { expect(resp.body).to.be.an('array'); - expect(resp.body.length).to.be(55); + expect(resp.body.length).to.be(21); // Test for sample data card expect(resp.body.findIndex((c: { id: string }) => c.id === 'sample_data_all')).to.be.above( diff --git a/test/functional/apps/dashboard/group1/edit_visualizations.js b/test/functional/apps/dashboard/group1/edit_visualizations.js index decd7441e1d45..f065748f09b00 100644 --- a/test/functional/apps/dashboard/group1/edit_visualizations.js +++ b/test/functional/apps/dashboard/group1/edit_visualizations.js @@ -191,7 +191,7 @@ export default function ({ getService, getPageObjects }) { it('should lose its connection to the dashboard when creating new visualization', async () => { await visualize.gotoVisualizationLandingPage(); await visualize.clickNewVisualization(); - await visualize.clickMarkdownWidget(); + await visualize.clickVisualBuilder(); await visualize.notLinkedToOriginatingApp(); // return to origin should not be present in save modal diff --git a/test/functional/apps/dashboard/group5/legacy_urls.ts b/test/functional/apps/dashboard/group5/legacy_urls.ts index 62ae63baa8fc8..0056817f3465f 100644 --- a/test/functional/apps/dashboard/group5/legacy_urls.ts +++ b/test/functional/apps/dashboard/group5/legacy_urls.ts @@ -86,13 +86,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('resolves markdown link', async () => { - await visualize.navigateToNewVisualization(); - await visualize.clickMarkdownWidget(); + await dashboard.gotoDashboardLandingPage(); + await dashboard.clickNewDashboard(); + await dashboardAddPanel.clickMarkdownQuickButton(); await visEditor.setMarkdownTxt(`[abc](#/dashboard/${testDashboardId})`); await visEditor.clickGo(); - - await visualize.saveVisualizationExpectSuccess('legacy url markdown'); - + await visualize.saveVisualization('legacy url markdown', { redirectToOrigin: true }); await (await find.byLinkText('abc')).click(); await header.waitUntilLoadingHasFinished(); diff --git a/test/functional/apps/dashboard/group6/dashboard_esql_no_data.ts b/test/functional/apps/dashboard/group6/dashboard_esql_no_data.ts index 148cb95a82b11..333ac7f015397 100644 --- a/test/functional/apps/dashboard/group6/dashboard_esql_no_data.ts +++ b/test/functional/apps/dashboard/group6/dashboard_esql_no_data.ts @@ -6,14 +6,16 @@ * your election, the "Elastic License 2.0", the "GNU Affero General Public * License v3.0 only", or the "Server Side Public License, v 1". */ +import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const kibanaServer = getService('kibanaServer'); const testSubjects = getService('testSubjects'); - const esql = getService('esql'); - const PageObjects = getPageObjects(['discover', 'dashboard']); + const panelActions = getService('dashboardPanelActions'); + const monacoEditor = getService('monacoEditor'); + const PageObjects = getPageObjects(['dashboard']); describe('No Data Views: Try ES|QL', () => { before(async () => { @@ -26,8 +28,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.existOrFail('noDataViewsPrompt'); await testSubjects.click('tryESQLLink'); - await PageObjects.discover.expectOnDiscover(); - await esql.expectEsqlStatement('FROM logs* | LIMIT 10'); + await PageObjects.dashboard.expectOnDashboard('New Dashboard'); + expect(await testSubjects.exists('lnsVisualizationContainer')).to.be(true); + + await panelActions.clickInlineEdit(); + const editorValue = await monacoEditor.getCodeEditorValue(); + expect(editorValue).to.eql(`FROM logs* | LIMIT 10`); }); }); } diff --git a/test/functional/apps/dashboard_elements/markdown/_markdown_vis.ts b/test/functional/apps/dashboard_elements/markdown/_markdown_vis.ts index 18402669293f7..33386a5cb6862 100644 --- a/test/functional/apps/dashboard_elements/markdown/_markdown_vis.ts +++ b/test/functional/apps/dashboard_elements/markdown/_markdown_vis.ts @@ -12,25 +12,26 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { - const { visualize, visEditor, visChart, header } = getPageObjects([ - 'visualize', + const { visEditor, visChart, header, dashboard } = getPageObjects([ + 'dashboard', 'visEditor', 'visChart', 'header', ]); const find = getService('find'); const inspector = getService('inspector'); + const dashboardAddPanel = getService('dashboardAddPanel'); const markdown = ` # Heading 1

Inline HTML that should not be rendered as html

`; - describe('markdown app in visualize app', () => { + describe('markdown app', () => { before(async function () { - await visualize.initTests(); - await visualize.navigateToNewVisualization(); - await visualize.clickMarkdownWidget(); + await dashboard.initTests(); + await dashboard.clickNewDashboard(); + await dashboardAddPanel.clickMarkdownQuickButton(); await visEditor.setMarkdownTxt(markdown); await visEditor.clickGo(); }); diff --git a/test/functional/apps/discover/context_awareness/extensions/_get_cell_renderers.ts b/test/functional/apps/discover/context_awareness/extensions/_get_cell_renderers.ts index cb66afc7ebc57..e18f6c5860dd2 100644 --- a/test/functional/apps/discover/context_awareness/extensions/_get_cell_renderers.ts +++ b/test/functional/apps/discover/context_awareness/extensions/_get_cell_renderers.ts @@ -105,8 +105,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const firstCell = await dataGrid.getCellElementExcludingControlColumns(0, 0); const lastCell = await dataGrid.getCellElementExcludingControlColumns(2, 0); - const firstServiceNameCell = await firstCell.findByTestSubject('serviceNameCell-java'); - const lastServiceNameCell = await lastCell.findByTestSubject('serviceNameCell-unknown'); + const firstServiceNameCell = await firstCell.findByTestSubject( + 'dataTableCellActionsPopover_service.name' + ); + const lastServiceNameCell = await lastCell.findByTestSubject( + 'dataTableCellActionsPopover_service.name' + ); expect(await firstServiceNameCell.getVisibleText()).to.be('product'); expect(await lastServiceNameCell.getVisibleText()).to.be('accounting'); }); @@ -130,7 +134,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await retry.try(async () => { const firstCell = await dataGrid.getCellElementExcludingControlColumns(0, 0); expect(await firstCell.getVisibleText()).to.be('product'); - await testSubjects.missingOrFail('*serviceNameCell*'); + await testSubjects.missingOrFail('dataTableCellActionsPopover_service.name'); }); }); }); @@ -278,8 +282,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await retry.try(async () => { firstCell = await dataGrid.getCellElementExcludingControlColumns(0, 1); lastCell = await dataGrid.getCellElementExcludingControlColumns(2, 1); - const firstServiceNameCell = await firstCell.findByTestSubject('serviceNameCell-java'); - const lastServiceNameCell = await lastCell.findByTestSubject('serviceNameCell-unknown'); + const firstServiceNameCell = await firstCell.findByTestSubject( + 'dataTableCellActionsPopover_service.name' + ); + const lastServiceNameCell = await lastCell.findByTestSubject( + 'dataTableCellActionsPopover_service.name' + ); expect(await firstServiceNameCell.getVisibleText()).to.be('product'); expect(await lastServiceNameCell.getVisibleText()).to.be('accounting'); }); @@ -309,7 +317,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await firstCell.getVisibleText()).to.be('product'); expect(await lastCell.getVisibleText()).to.be('accounting'); - await testSubjects.missingOrFail('*serviceNameCell*'); + await testSubjects.missingOrFail('dataTableCellActionsPopover_service.name'); }); }); }); diff --git a/test/functional/apps/discover/group3/_lens_vis.ts b/test/functional/apps/discover/group3/_lens_vis.ts index 1bd6f8099fd22..db526fe978610 100644 --- a/test/functional/apps/discover/group3/_lens_vis.ts +++ b/test/functional/apps/discover/group3/_lens_vis.ts @@ -110,7 +110,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { return seriesType; } - // Failing: See https://github.com/elastic/kibana/issues/184600 + // Failing: See https://github.com/elastic/kibana/issues/197342 describe.skip('discover lens vis', function () { before(async () => { await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader']); diff --git a/test/functional/apps/visualize/group1/_chart_types.ts b/test/functional/apps/visualize/group1/_chart_types.ts index 5b38fb26173b0..a8f5b7294382a 100644 --- a/test/functional/apps/visualize/group1/_chart_types.ts +++ b/test/functional/apps/visualize/group1/_chart_types.ts @@ -22,14 +22,18 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await visualize.navigateToNewVisualization(); }); - it('should show the promoted vis types for the first step', async function () { - const expectedChartTypes = ['Custom visualization', 'Lens', 'Maps', 'TSVB']; + it('should show the expected visualizations types for both recommended and legacy tabs', async function () { + const expectedRecommendedChartTypes = ['Custom visualization', 'Lens', 'Maps']; + const expectedLegacyChartTypes = ['Aggregation-based', 'TSVB']; // find all the chart types and make sure there all there - const chartTypes = (await visualize.getPromotedVisTypes()).sort(); + const chartTypes = await visualize.getVisibleVisTypes(); log.debug('returned chart types = ' + chartTypes); - log.debug('expected chart types = ' + expectedChartTypes); - expect(chartTypes).to.eql(expectedChartTypes); + log.debug('expected chart types = ' + expectedRecommendedChartTypes); + expect(chartTypes).to.eql(expectedRecommendedChartTypes); + await visualize.clickLegacyTab(); + const legacyChartTypes = await visualize.getVisibleVisTypes(); + expect(legacyChartTypes).to.eql(expectedLegacyChartTypes); }); it('should show the correct agg based chart types', async function () { diff --git a/test/functional/apps/visualize/group3/_visualize_listing.ts b/test/functional/apps/visualize/group3/_visualize_listing.ts index 7cb1b7b4b51be..19fba122b4cad 100644 --- a/test/functional/apps/visualize/group3/_visualize_listing.ts +++ b/test/functional/apps/visualize/group3/_visualize_listing.ts @@ -10,7 +10,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { - const { visualize, visEditor } = getPageObjects(['visualize', 'visEditor']); + const { visualize, visualBuilder } = getPageObjects(['visualize', 'visualBuilder']); const listingTable = getService('listingTable'); describe('visualize listing page', function describeIndexTests() { @@ -24,18 +24,18 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('create new viz', async function () { - // type markdown is used for simplicity - await visualize.createSimpleMarkdownViz(vizName); + // type tsvb is used for simplicity + await visualize.createSimpleTSVBViz(vizName); await visualize.gotoVisualizationLandingPage(); await listingTable.expectItemsCount('visualize', 1); }); it('delete all viz', async function () { - await visualize.createSimpleMarkdownViz(vizName + '1'); + await visualize.createSimpleTSVBViz(vizName + '1'); await visualize.gotoVisualizationLandingPage(); await listingTable.expectItemsCount('visualize', 2); - await visualize.createSimpleMarkdownViz(vizName + '2'); + await visualize.createSimpleTSVBViz(vizName + '2'); await visualize.gotoVisualizationLandingPage(); await listingTable.expectItemsCount('visualize', 3); @@ -48,9 +48,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { before(async function () { // create one new viz await visualize.navigateToNewVisualization(); - await visualize.clickMarkdownWidget(); - await visEditor.setMarkdownTxt('HELLO'); - await visEditor.clickGo(); + await visualize.clickVisualBuilder(); + await visualBuilder.checkVisualBuilderIsPresent(); await visualize.saveVisualization('Hello World'); await visualize.gotoVisualizationLandingPage(); }); diff --git a/test/functional/page_objects/visualize_page.ts b/test/functional/page_objects/visualize_page.ts index 64f6858a1f759..1f6c9cc11c474 100644 --- a/test/functional/page_objects/visualize_page.ts +++ b/test/functional/page_objects/visualize_page.ts @@ -41,7 +41,6 @@ export class VisualizePageObject extends FtrService { private readonly elasticChart = this.ctx.getService('elasticChart'); private readonly common = this.ctx.getPageObject('common'); private readonly header = this.ctx.getPageObject('header'); - private readonly visEditor = this.ctx.getPageObject('visEditor'); private readonly visChart = this.ctx.getPageObject('visChart'); private readonly toasts = this.ctx.getService('toasts'); @@ -106,7 +105,8 @@ export class VisualizePageObject extends FtrService { } public async clickAggBasedVisualizations() { - await this.testSubjects.click('visGroupAggBasedExploreLink'); + await this.clickLegacyTab(); + await this.testSubjects.click('visType-aggbased'); } public async goBackToGroups() { @@ -125,7 +125,7 @@ export class VisualizePageObject extends FtrService { .map((chart) => $(chart).findTestSubject('visTypeTitle').text().trim()); } - public async getPromotedVisTypes() { + public async getVisibleVisTypes() { const chartTypeField = await this.testSubjects.find('visNewDialogGroups'); const $ = await chartTypeField.parseDomContent(); const promotedVisTypes: string[] = []; @@ -137,7 +137,7 @@ export class VisualizePageObject extends FtrService { promotedVisTypes.push(title); } }); - return promotedVisTypes; + return promotedVisTypes.sort(); } public async waitForVisualizationSelectPage() { @@ -221,8 +221,8 @@ export class VisualizePageObject extends FtrService { await this.clickVisType('line'); } - public async clickMarkdownWidget() { - await this.clickVisType('markdown'); + public async clickLegacyTab() { + await this.testSubjects.click('groupModalLegacyTab'); } public async clickMetric() { @@ -254,6 +254,7 @@ export class VisualizePageObject extends FtrService { } public async clickVisualBuilder() { + await this.clickLegacyTab(); await this.clickVisType('metrics'); } @@ -281,12 +282,10 @@ export class VisualizePageObject extends FtrService { return await this.hasVisType('maps'); } - public async createSimpleMarkdownViz(vizName: string) { + public async createSimpleTSVBViz(vizName: string) { await this.gotoVisualizationLandingPage(); await this.navigateToNewVisualization(); - await this.clickMarkdownWidget(); - await this.visEditor.setMarkdownTxt(vizName); - await this.visEditor.clickGo(); + await this.clickVisualBuilder(); await this.saveVisualization(vizName); } diff --git a/test/plugin_functional/test_suites/core/deprecations.ts b/test/plugin_functional/test_suites/core/deprecations.ts index 081209e6c7dce..b41adc7ffefa5 100644 --- a/test/plugin_functional/test_suites/core/deprecations.ts +++ b/test/plugin_functional/test_suites/core/deprecations.ts @@ -151,7 +151,7 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide }); expect(resolveResult).to.eql({ - reason: 'This deprecation cannot be resolved automatically.', + reason: 'This deprecation cannot be resolved automatically or marked as resolved.', status: 'fail', }); }); diff --git a/tsconfig.base.json b/tsconfig.base.json index 09d1f31eceb23..783e4c254b89b 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1188,6 +1188,8 @@ "@kbn/management-storybook-config/*": ["packages/kbn-management/storybook/config/*"], "@kbn/management-test-plugin": ["test/plugin_functional/plugins/management_test_plugin"], "@kbn/management-test-plugin/*": ["test/plugin_functional/plugins/management_test_plugin/*"], + "@kbn/manifest": ["packages/kbn-manifest"], + "@kbn/manifest/*": ["packages/kbn-manifest/*"], "@kbn/mapbox-gl": ["packages/kbn-mapbox-gl"], "@kbn/mapbox-gl/*": ["packages/kbn-mapbox-gl/*"], "@kbn/maps-custom-raster-source-plugin": ["x-pack/examples/third_party_maps_source_example"], @@ -1450,6 +1452,8 @@ "@kbn/resolver-test-plugin/*": ["x-pack/test/plugin_functional/plugins/resolver_test/*"], "@kbn/response-ops-feature-flag-service": ["packages/response-ops/feature_flag_service"], "@kbn/response-ops-feature-flag-service/*": ["packages/response-ops/feature_flag_service/*"], + "@kbn/response-ops-rule-params": ["packages/response-ops/rule_params"], + "@kbn/response-ops-rule-params/*": ["packages/response-ops/rule_params/*"], "@kbn/response-stream-plugin": ["examples/response_stream"], "@kbn/response-stream-plugin/*": ["examples/response_stream/*"], "@kbn/rison": ["packages/kbn-rison"], diff --git a/versions.json b/versions.json index ff0e6d254f24e..ac2bcf4de9b8f 100644 --- a/versions.json +++ b/versions.json @@ -24,7 +24,7 @@ "previousMajor": true }, { - "version": "7.17.25", + "version": "7.17.26", "branch": "7.17" } ] diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.test.tsx index 08bac25c0a522..1ef2db7b26c03 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.test.tsx @@ -24,6 +24,7 @@ import { Conversation } from '../assistant_context/types'; import * as all from './chat_send/use_chat_send'; import { useConversation } from './use_conversation'; import { AIConnector } from '../connectorland/connector_selector'; +import { omit } from 'lodash'; jest.mock('../connectorland/use_load_connectors'); jest.mock('../connectorland/connector_setup'); @@ -140,6 +141,112 @@ describe('Assistant', () => { >); }); + describe('persistent storage', () => { + it('should refetchCurrentUserConversations after settings save button click', async () => { + const chatSendSpy = jest.spyOn(all, 'useChatSend'); + await renderAssistant(); + + fireEvent.click(screen.getByTestId('settings')); + + jest.mocked(useFetchCurrentUserConversations).mockReturnValue({ + data: { + ...mockData, + welcome_id: { + ...mockData.welcome_id, + apiConfig: { newProp: true }, + }, + }, + isLoading: false, + refetch: jest.fn().mockResolvedValue({ + isLoading: false, + data: { + ...mockData, + welcome_id: { + ...mockData.welcome_id, + apiConfig: { newProp: true }, + }, + }, + }), + isFetched: true, + } as unknown as DefinedUseQueryResult, unknown>); + + await act(async () => { + fireEvent.click(screen.getByTestId('save-button')); + }); + + expect(chatSendSpy).toHaveBeenLastCalledWith( + expect.objectContaining({ + currentConversation: { + apiConfig: { newProp: true }, + category: 'assistant', + id: mockData.welcome_id.id, + messages: [], + title: 'Welcome', + replacements: {}, + }, + }) + ); + }); + + it('should refetchCurrentUserConversations after settings save button click, but do not update convos when refetch returns bad results', async () => { + jest.mocked(useFetchCurrentUserConversations).mockReturnValue({ + data: mockData, + isLoading: false, + refetch: jest.fn().mockResolvedValue({ + isLoading: false, + data: omit(mockData, 'welcome_id'), + }), + isFetched: true, + } as unknown as DefinedUseQueryResult, unknown>); + const chatSendSpy = jest.spyOn(all, 'useChatSend'); + await renderAssistant(); + + fireEvent.click(screen.getByTestId('settings')); + await act(async () => { + fireEvent.click(screen.getByTestId('save-button')); + }); + + expect(chatSendSpy).toHaveBeenLastCalledWith( + expect.objectContaining({ + currentConversation: { + apiConfig: { connectorId: '123' }, + replacements: {}, + category: 'assistant', + id: mockData.welcome_id.id, + messages: [], + title: 'Welcome', + }, + }) + ); + }); + + it('should delete conversation when delete button is clicked', async () => { + await renderAssistant(); + const deleteButton = screen.getAllByTestId('delete-option')[0]; + await act(async () => { + fireEvent.click(deleteButton); + }); + + await act(async () => { + fireEvent.click(screen.getByTestId('confirmModalConfirmButton')); + }); + + await waitFor(() => { + expect(mockDeleteConvo).toHaveBeenCalledWith(mockData.electric_sheep_id.id); + }); + }); + it('should refetchCurrentUserConversations after clear chat history button click', async () => { + await renderAssistant(); + fireEvent.click(screen.getByTestId('chat-context-menu')); + fireEvent.click(screen.getByTestId('clear-chat')); + fireEvent.click(screen.getByTestId('confirmModalConfirmButton')); + await waitFor(() => { + expect(clearConversation).toHaveBeenCalled(); + expect(refetchResults).toHaveBeenCalled(); + }); + }); + }); + describe('when selected conversation changes and some connectors are loaded', () => { it('should persist the conversation id to local storage', async () => { const getConversation = jest.fn().mockResolvedValue(mockData.electric_sheep_id); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings.test.tsx index 14bfcb4cdbbec..c9f4f07d83b11 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings.test.tsx @@ -38,7 +38,7 @@ const mockContext = { basePromptContexts: MOCK_QUICK_PROMPTS, setSelectedSettingsTab, http: {}, - assistantFeatures: { assistantModelEvaluation: true }, + assistantFeatures: { assistantModelEvaluation: true, assistantKnowledgeBaseByDefault: false }, selectedSettingsTab: 'CONVERSATIONS_TAB', assistantAvailability: { isAssistantEnabled: true, @@ -136,6 +136,17 @@ describe('AssistantSettings', () => { QUICK_PROMPTS_TAB, SYSTEM_PROMPTS_TAB, ])('%s', (tab) => { + it('Opens the tab on button click', () => { + (useAssistantContext as jest.Mock).mockImplementation(() => ({ + ...mockContext, + selectedSettingsTab: tab === CONVERSATIONS_TAB ? ANONYMIZATION_TAB : CONVERSATIONS_TAB, + })); + const { getByTestId } = render(, { + wrapper, + }); + fireEvent.click(getByTestId(`${tab}-button`)); + expect(setSelectedSettingsTab).toHaveBeenCalledWith(tab); + }); it('renders with the correct tab open', () => { (useAssistantContext as jest.Mock).mockImplementation(() => ({ ...mockContext, diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings.tsx index f325e411bae2b..350780ea5b168 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings.tsx @@ -9,10 +9,14 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { EuiButton, EuiButtonEmpty, + EuiIcon, EuiModal, EuiModalFooter, + EuiKeyPadMenu, + EuiKeyPadMenuItem, EuiPage, EuiPageBody, + EuiPageSidebar, EuiSplitPanel, } from '@elastic/eui'; @@ -76,7 +80,16 @@ export const AssistantSettings: React.FC = React.memo( conversations, conversationsLoaded, }) => { - const { http, toasts, selectedSettingsTab, setSelectedSettingsTab } = useAssistantContext(); + const { + assistantFeatures: { + assistantModelEvaluation: modelEvaluatorEnabled, + assistantKnowledgeBaseByDefault, + }, + http, + toasts, + selectedSettingsTab, + setSelectedSettingsTab, + } = useAssistantContext(); useEffect(() => { if (selectedSettingsTab == null) { @@ -201,6 +214,115 @@ export const AssistantSettings: React.FC = React.memo( return ( + {!assistantKnowledgeBaseByDefault && ( + + + setSelectedSettingsTab(CONVERSATIONS_TAB)} + data-test-subj={`${CONVERSATIONS_TAB}-button`} + > + <> + + + + + setSelectedSettingsTab(QUICK_PROMPTS_TAB)} + data-test-subj={`${QUICK_PROMPTS_TAB}-button`} + > + <> + + + + + setSelectedSettingsTab(SYSTEM_PROMPTS_TAB)} + data-test-subj={`${SYSTEM_PROMPTS_TAB}-button`} + > + + + + setSelectedSettingsTab(ANONYMIZATION_TAB)} + data-test-subj={`${ANONYMIZATION_TAB}-button`} + > + + + setSelectedSettingsTab(KNOWLEDGE_BASE_TAB)} + data-test-subj={`${KNOWLEDGE_BASE_TAB}-button`} + > + + + {modelEvaluatorEnabled && ( + setSelectedSettingsTab(EVALUATION_TAB)} + data-test-subj={`${EVALUATION_TAB}-button`} + > + + + )} + + + )} + { const original = jest.requireActual('../../assistant_context'); @@ -57,6 +59,13 @@ describe('AssistantSettingsButton', () => { jest.clearAllMocks(); }); + it('Clicking the settings gear opens the conversations tab', () => { + const { getByTestId } = render(); + fireEvent.click(getByTestId('settings')); + expect(setSelectedSettingsTab).toHaveBeenCalledWith(CONVERSATIONS_TAB); + expect(setIsSettingsModalVisible).toHaveBeenCalledWith(true); + }); + it('Settings modal is visible and calls correct actions per click', () => { const { getByTestId } = render( diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings_button.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings_button.tsx index 40bf1e740ab60..3d6544643ba3e 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings_button.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings_button.tsx @@ -6,6 +6,7 @@ */ import React, { useCallback } from 'react'; +import { EuiButtonIcon, EuiToolTip } from '@elastic/eui'; import { QueryObserverResult, RefetchOptions, RefetchQueryFilters } from '@tanstack/react-query'; import { DataStreamApis } from '../use_data_stream_apis'; import { AIConnector } from '../../connectorland/connector_selector'; @@ -13,6 +14,7 @@ import { Conversation } from '../../..'; import { AssistantSettings } from './assistant_settings'; import * as i18n from './translations'; import { useAssistantContext } from '../../assistant_context'; +import { CONVERSATIONS_TAB } from './const'; interface Props { defaultConnector?: AIConnector; @@ -45,7 +47,11 @@ export const AssistantSettingsButton: React.FC = React.memo( refetchCurrentUserConversations, refetchPrompts, }) => { - const { toasts } = useAssistantContext(); + const { + assistantFeatures: { assistantKnowledgeBaseByDefault }, + toasts, + setSelectedSettingsTab, + } = useAssistantContext(); // Modal control functions const cleanupAndCloseModal = useCallback(() => { @@ -73,18 +79,39 @@ export const AssistantSettingsButton: React.FC = React.memo( [cleanupAndCloseModal, refetchCurrentUserConversations, refetchPrompts, toasts] ); + const handleShowConversationSettings = useCallback(() => { + setSelectedSettingsTab(CONVERSATIONS_TAB); + setIsSettingsModalVisible(true); + }, [setIsSettingsModalVisible, setSelectedSettingsTab]); + return ( - isSettingsModalVisible && ( - - ) + <> + {!assistantKnowledgeBaseByDefault && ( + + + + )} + + {isSettingsModalVisible && ( + + )} + ); } ); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index_entry_editor.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index_entry_editor.test.tsx index d4634cdf4c563..e4656b10d1d31 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index_entry_editor.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index_entry_editor.test.tsx @@ -42,10 +42,10 @@ describe('IndexEntryEditor', () => { jest.clearAllMocks(); }); - it('renders the form fields with initial values', () => { + it('renders the form fields with initial values', async () => { const { getByDisplayValue } = render(); - waitFor(() => { + await waitFor(() => { expect(getByDisplayValue('Test Entry')).toBeInTheDocument(); expect(getByDisplayValue('Test Description')).toBeInTheDocument(); expect(getByDisplayValue('Test Query Description')).toBeInTheDocument(); @@ -54,35 +54,37 @@ describe('IndexEntryEditor', () => { }); }); - it('updates the name field on change', () => { + it('updates the name field on change', async () => { const { getByTestId } = render(); - waitFor(() => { + await waitFor(() => { const nameInput = getByTestId('entry-name'); fireEvent.change(nameInput, { target: { value: 'New Entry Name' } }); + expect(mockSetEntry).toHaveBeenCalledWith(expect.any(Function)); }); - - expect(mockSetEntry).toHaveBeenCalledWith(expect.any(Function)); }); - it('updates the description field on change', () => { + it('updates the description field on change', async () => { const { getByTestId } = render(); - waitFor(() => { + + await waitFor(() => { const descriptionInput = getByTestId('entry-description'); fireEvent.change(descriptionInput, { target: { value: 'New Description' } }); }); - expect(mockSetEntry).toHaveBeenCalledWith(expect.any(Function)); + await waitFor(() => { + expect(mockSetEntry).toHaveBeenCalledWith(expect.any(Function)); + }); }); - it('updates the query description field on change', () => { + it('updates the query description field on change', async () => { const { getByTestId } = render(); - waitFor(() => { + + await waitFor(() => { const queryDescriptionInput = getByTestId('query-description'); fireEvent.change(queryDescriptionInput, { target: { value: 'New Query Description' } }); + expect(mockSetEntry).toHaveBeenCalledWith(expect.any(Function)); }); - - expect(mockSetEntry).toHaveBeenCalledWith(expect.any(Function)); }); it('displays sharing options and updates on selection', async () => { @@ -91,8 +93,6 @@ describe('IndexEntryEditor', () => { await waitFor(() => { fireEvent.click(getByTestId('sharing-select')); fireEvent.click(getByTestId('sharing-private-option')); - }); - await waitFor(() => { expect(mockSetEntry).toHaveBeenCalledWith(expect.any(Function)); }); }); @@ -100,28 +100,25 @@ describe('IndexEntryEditor', () => { it('fetches index options and updates on selection', async () => { const { getAllByTestId, getByTestId } = render(); - await waitFor(() => expect(mockDataViews.getIndices).toHaveBeenCalled()); - await waitFor(() => { + expect(mockDataViews.getIndices).toHaveBeenCalled(); fireEvent.click(getByTestId('index-combobox')); fireEvent.click(getAllByTestId('comboBoxToggleListButton')[0]); + fireEvent.click(getByTestId('index-2')); + expect(mockSetEntry).toHaveBeenCalledWith(expect.any(Function)); }); - fireEvent.click(getByTestId('index-2')); - - expect(mockSetEntry).toHaveBeenCalledWith(expect.any(Function)); }); it('fetches field options based on selected index and updates on selection', async () => { const { getByTestId, getAllByTestId } = render(); - await waitFor(() => + await waitFor(() => { expect(mockDataViews.getFieldsForWildcard).toHaveBeenCalledWith({ pattern: 'index-1', - fieldTypes: ['semantic_text'], - }) - ); + }); + }); - await waitFor(() => { + await waitFor(async () => { fireEvent.click(getByTestId('index-combobox')); fireEvent.click(getAllByTestId('comboBoxToggleListButton')[0]); }); @@ -135,7 +132,10 @@ describe('IndexEntryEditor', () => { within(getByTestId('entry-combobox')).getByTestId('comboBoxSearchInput'), 'field-3' ); - expect(mockSetEntry).toHaveBeenCalledWith(expect.any(Function)); + + await waitFor(() => { + expect(mockSetEntry).toHaveBeenCalledWith(expect.any(Function)); + }); }); it('disables the field combo box if no index is selected', () => { diff --git a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index_entry_editor.tsx b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index_entry_editor.tsx index 7475ea55ca5fc..550861bcbffd9 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index_entry_editor.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/index_entry_editor.tsx @@ -17,7 +17,7 @@ import { EuiSuperSelect, } from '@elastic/eui'; import useAsync from 'react-use/lib/useAsync'; -import React, { useCallback } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { IndexEntry } from '@kbn/elastic-assistant-common'; import { DataViewsContract } from '@kbn/data-views-plugin/public'; import * as i18n from './translations'; @@ -96,29 +96,37 @@ export const IndexEntryEditor: React.FC = React.memo( })); }, [dataViews]); - const fieldOptions = useAsync(async () => { - const fields = await dataViews.getFieldsForWildcard({ - pattern: entry?.index ?? '', - fieldTypes: ['semantic_text'], - }); + const indexFields = useAsync( + async () => + dataViews.getFieldsForWildcard({ + pattern: entry?.index ?? '', + }), + [] + ); - return fields - .filter((field) => field.esTypes?.includes('semantic_text')) - .map((field) => ({ + const fieldOptions = useMemo( + () => + indexFields?.value + ?.filter((field) => field.esTypes?.includes('semantic_text')) + .map((field) => ({ + 'data-test-subj': field.name, + label: field.name, + value: field.name, + })) ?? [], + [indexFields?.value] + ); + + const outputFieldOptions = useMemo( + () => + indexFields?.value?.map((field) => ({ 'data-test-subj': field.name, label: field.name, value: field.name, - })); - }, [entry]); - - const setIndex = useCallback( - async (e: Array>) => { - setEntry((prevEntry) => ({ ...prevEntry, index: e[0]?.value })); - }, - [setEntry] + })) ?? [], + [indexFields?.value] ); - const onCreateOption = (searchValue: string) => { + const onCreateIndexOption = (searchValue: string) => { const normalizedSearchValue = searchValue.trim().toLowerCase(); if (!normalizedSearchValue) { @@ -131,7 +139,6 @@ export const IndexEntryEditor: React.FC = React.memo( }; setIndex([newOption]); - setField([{ label: '', value: '' }]); }; const onCreateFieldOption = (searchValue: string) => { @@ -170,6 +177,52 @@ export const IndexEntryEditor: React.FC = React.memo( [setEntry] ); + // Field + const setOutputFields = useCallback( + async (e: Array>) => { + setEntry((prevEntry) => ({ + ...prevEntry, + outputFields: e + ?.filter((option) => !!option.value) + .map((option) => option.value as string), + })); + }, + [setEntry] + ); + + const setIndex = useCallback( + async (e: Array>) => { + setEntry((prevEntry) => ({ ...prevEntry, index: e[0]?.value })); + setField([]); + setOutputFields([]); + }, + [setEntry, setField, setOutputFields] + ); + + const onCreateOutputFieldsOption = useCallback( + (searchValue: string) => { + const normalizedSearchValue = searchValue.trim().toLowerCase(); + + if (!normalizedSearchValue) { + return; + } + + const newOption: EuiComboBoxOptionOption = { + label: searchValue, + value: searchValue, + }; + + setOutputFields([ + ...(entry?.outputFields?.map((field) => ({ + label: field, + value: field, + })) ?? []), + newOption, + ]); + }, + [entry?.outputFields, setOutputFields] + ); + return ( = React.memo( aria-label={i18n.ENTRY_INDEX_NAME_INPUT_LABEL} isClearable={true} singleSelection={{ asPlainText: true }} - onCreateOption={onCreateOption} + onCreateOption={onCreateIndexOption} fullWidth options={indexOptions.value ?? []} selectedOptions={ @@ -228,7 +281,7 @@ export const IndexEntryEditor: React.FC = React.memo( singleSelection={{ asPlainText: true }} onCreateOption={onCreateFieldOption} fullWidth - options={fieldOptions.value ?? []} + options={fieldOptions} selectedOptions={ entry?.field ? [ @@ -281,11 +334,17 @@ export const IndexEntryEditor: React.FC = React.memo( ({ + label: field, + value: field, + })) ?? [] + } + onChange={setOutputFields} /> diff --git a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/translations.ts b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/translations.ts index 077426884eb8a..34e158ac699cc 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/translations.ts +++ b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/translations.ts @@ -168,20 +168,6 @@ export const ENTRY_NAME_INPUT_PLACEHOLDER = i18n.translate( } ); -export const ENTRY_SPACE_INPUT_LABEL = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entrySpaceInputLabel', - { - defaultMessage: 'Space', - } -); - -export const ENTRY_SPACE_INPUT_PLACEHOLDER = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entrySpaceInputPlaceholder', - { - defaultMessage: 'Select', - } -); - export const SHARING_PRIVATE_OPTION_LABEL = i18n.translate( 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.sharingPrivateOptionLabel', { @@ -272,7 +258,8 @@ export const ENTRY_DESCRIPTION_HELP_LABEL = i18n.translate( export const ENTRY_DESCRIPTION_PLACEHOLDER = i18n.translate( 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryDescriptionPlaceholder', { - defaultMessage: 'Use this index to answer any question related to asset information.', + defaultMessage: + 'Example: "Use this index to answer any question related to asset information."', } ); @@ -295,7 +282,7 @@ export const ENTRY_QUERY_DESCRIPTION_PLACEHOLDER = i18n.translate( 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryQueryDescriptionPlaceholder', { defaultMessage: - 'Key terms to retrieve asset related information, like host names, IP Addresses or cloud objects.', + 'Example: "Key terms to retrieve asset related information, like host names, IP Addresses or cloud objects."', } ); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/use_knowledge_base_table.tsx b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/use_knowledge_base_table.tsx index 67157b3ae7b12..d27853f6e8625 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/use_knowledge_base_table.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/use_knowledge_base_table.tsx @@ -16,6 +16,7 @@ import { } from '@kbn/elastic-assistant-common'; import useAsync from 'react-use/lib/useAsync'; +import { UserProfileAvatarData } from '@kbn/user-profile-components'; import { useAssistantContext } from '../../..'; import * as i18n from './translations'; import { BadgesColumn } from '../../assistant/common/components/assistant_settings_management/badges'; @@ -23,14 +24,21 @@ import { useInlineActions } from '../../assistant/common/components/assistant_se import { isSystemEntry } from './helpers'; const AuthorColumn = ({ entry }: { entry: KnowledgeBaseEntryResponse }) => { - const { currentUserAvatar, userProfileService } = useAssistantContext(); + const { userProfileService } = useAssistantContext(); const userProfile = useAsync(async () => { - const profile = await userProfileService?.bulkGet({ uids: new Set([entry.createdBy]) }); - return profile?.[0].user.username; - }, []); + const profile = await userProfileService?.bulkGet<{ avatar: UserProfileAvatarData }>({ + uids: new Set([entry.createdBy]), + dataPath: 'avatar', + }); + return { username: profile?.[0].user.username, avatar: profile?.[0].data.avatar }; + }, [entry.createdBy]); - const userName = useMemo(() => userProfile?.value ?? 'Unknown', [userProfile?.value]); + const userName = useMemo( + () => userProfile?.value?.username ?? 'Unknown', + [userProfile?.value?.username] + ); + const userAvatar = userProfile.value?.avatar; const badgeItem = isSystemEntry(entry) ? 'Elastic' : userName; const userImage = isSystemEntry(entry) ? ( { margin-right: 14px; `} /> - ) : currentUserAvatar?.imageUrl != null ? ( + ) : userAvatar?.imageUrl != null ? ( { ) : ( { }, { name: i18n.COLUMN_AUTHOR, - sortable: ({ users }: KnowledgeBaseEntryResponse) => users[0]?.name, render: (entry: KnowledgeBaseEntryResponse) => , }, { diff --git a/x-pack/packages/kbn-elastic-assistant/tsconfig.json b/x-pack/packages/kbn-elastic-assistant/tsconfig.json index 8d19fa86f4d11..f1dde6cbeafee 100644 --- a/x-pack/packages/kbn-elastic-assistant/tsconfig.json +++ b/x-pack/packages/kbn-elastic-assistant/tsconfig.json @@ -31,5 +31,6 @@ "@kbn/core", "@kbn/zod", "@kbn/data-views-plugin", + "@kbn/user-profile-components", ] } diff --git a/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts b/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts index 9fd3483771a9f..c2eb7d0ed8ef3 100644 --- a/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts +++ b/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts @@ -287,6 +287,16 @@ export type InferenceServiceSettings = }; }; } + | { + service: 'watsonxai'; + service_settings: { + api_key: string; + url: string; + model_id: string; + project_id: string; + api_version: string; + }; + } | { service: 'amazonbedrock'; service_settings: { diff --git a/x-pack/packages/security-solution/features/src/assistant/kibana_features.ts b/x-pack/packages/security-solution/features/src/assistant/kibana_features.ts index e7bafd5595316..81cf7d18af129 100644 --- a/x-pack/packages/security-solution/features/src/assistant/kibana_features.ts +++ b/x-pack/packages/security-solution/features/src/assistant/kibana_features.ts @@ -26,6 +26,9 @@ export const getAssistantBaseKibanaFeature = (): BaseKibanaFeatureConfig => ({ app: [ASSISTANT_FEATURE_ID, 'kibana'], catalogue: [APP_ID], minimumLicense: 'enterprise', + management: { + kibana: ['securityAiAssistantManagement'], + }, privileges: { all: { api: ['elasticAssistant'], @@ -36,6 +39,9 @@ export const getAssistantBaseKibanaFeature = (): BaseKibanaFeatureConfig => ({ read: [], }, ui: [], + management: { + kibana: ['securityAiAssistantManagement'], + }, }, read: { // No read-only mode currently supported diff --git a/x-pack/packages/security-solution/upselling/messages/index.tsx b/x-pack/packages/security-solution/upselling/messages/index.tsx index 722a711995d01..4bda9477f13c0 100644 --- a/x-pack/packages/security-solution/upselling/messages/index.tsx +++ b/x-pack/packages/security-solution/upselling/messages/index.tsx @@ -46,3 +46,11 @@ export const ALERT_SUPPRESSION_RULE_DETAILS = i18n.translate( 'Alert suppression is configured but will not be applied due to insufficient licensing', } ); + +export const UPGRADE_NOTES_MANAGEMENT_USER_FILTER = (requiredLicense: string) => + i18n.translate('securitySolutionPackages.noteManagement.userFilter.upsell', { + defaultMessage: 'Upgrade to {requiredLicense} to make use of user filters', + values: { + requiredLicense, + }, + }); diff --git a/x-pack/packages/security-solution/upselling/service/types.ts b/x-pack/packages/security-solution/upselling/service/types.ts index 43019271a7e02..b053c9aedf857 100644 --- a/x-pack/packages/security-solution/upselling/service/types.ts +++ b/x-pack/packages/security-solution/upselling/service/types.ts @@ -27,4 +27,5 @@ export type UpsellingMessageId = | 'investigation_guide_interactions' | 'alert_assignments' | 'alert_suppression_rule_form' - | 'alert_suppression_rule_details'; + | 'alert_suppression_rule_details' + | 'note_management_user_filter'; diff --git a/x-pack/plugins/actions/server/lib/retry_if_conflicts.ts b/x-pack/plugins/actions/server/lib/retry_if_conflicts.ts index 4778e1ced1013..bae6bd95a682f 100644 --- a/x-pack/plugins/actions/server/lib/retry_if_conflicts.ts +++ b/x-pack/plugins/actions/server/lib/retry_if_conflicts.ts @@ -20,7 +20,7 @@ export const RetryForConflictsAttempts = 2; // note: we considered making this random, to help avoid a stampede, but // with 1 retry it probably doesn't matter, and adding randomness could // make it harder to diagnose issues -const RetryForConflictsDelay = 250; +const RetryForConflictsDelay = 100; // retry an operation if it runs into 409 Conflict's, up to a limit export async function retryIfConflicts( diff --git a/x-pack/plugins/actions/server/routes/legacy/create.ts b/x-pack/plugins/actions/server/routes/legacy/create.ts index 6da0d25c2e59c..f667a9e003a77 100644 --- a/x-pack/plugins/actions/server/routes/legacy/create.ts +++ b/x-pack/plugins/actions/server/routes/legacy/create.ts @@ -38,6 +38,7 @@ export const createActionRoute = ( access: 'public', summary: `Create a connector`, tags: ['oas-tag:connectors'], + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, validate: { diff --git a/x-pack/plugins/actions/server/routes/legacy/delete.ts b/x-pack/plugins/actions/server/routes/legacy/delete.ts index 2204095e03801..c7e1e985cc6f0 100644 --- a/x-pack/plugins/actions/server/routes/legacy/delete.ts +++ b/x-pack/plugins/actions/server/routes/legacy/delete.ts @@ -32,6 +32,7 @@ export const deleteActionRoute = ( summary: `Delete a connector`, description: 'WARNING: When you delete a connector, it cannot be recovered.', tags: ['oas-tag:connectors'], + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, validate: { diff --git a/x-pack/plugins/actions/server/routes/legacy/execute.ts b/x-pack/plugins/actions/server/routes/legacy/execute.ts index 8083f884c1186..71b04262075d4 100644 --- a/x-pack/plugins/actions/server/routes/legacy/execute.ts +++ b/x-pack/plugins/actions/server/routes/legacy/execute.ts @@ -37,6 +37,7 @@ export const executeActionRoute = ( options: { access: 'public', summary: `Run a connector`, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, tags: ['oas-tag:connectors'], }, diff --git a/x-pack/plugins/actions/server/routes/legacy/get.ts b/x-pack/plugins/actions/server/routes/legacy/get.ts index 2adf6413b9248..571849ccaf478 100644 --- a/x-pack/plugins/actions/server/routes/legacy/get.ts +++ b/x-pack/plugins/actions/server/routes/legacy/get.ts @@ -31,6 +31,7 @@ export const getActionRoute = ( options: { access: 'public', summary: `Get connector information`, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, tags: ['oas-tag:connectors'], }, diff --git a/x-pack/plugins/actions/server/routes/legacy/get_all.ts b/x-pack/plugins/actions/server/routes/legacy/get_all.ts index 04ba20f4fb3c8..f0a17acb96691 100644 --- a/x-pack/plugins/actions/server/routes/legacy/get_all.ts +++ b/x-pack/plugins/actions/server/routes/legacy/get_all.ts @@ -23,6 +23,7 @@ export const getAllActionRoute = ( options: { access: 'public', summary: `Get all connectors`, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, tags: ['oas-tag:connectors'], }, diff --git a/x-pack/plugins/actions/server/routes/legacy/list_action_types.ts b/x-pack/plugins/actions/server/routes/legacy/list_action_types.ts index f42cd7479286c..cc3e9c23f240d 100644 --- a/x-pack/plugins/actions/server/routes/legacy/list_action_types.ts +++ b/x-pack/plugins/actions/server/routes/legacy/list_action_types.ts @@ -27,6 +27,7 @@ export const listActionTypesRoute = ( options: { access: 'public', summary: `Get connector types`, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, tags: ['oas-tag:connectors'], }, diff --git a/x-pack/plugins/actions/server/routes/legacy/update.ts b/x-pack/plugins/actions/server/routes/legacy/update.ts index 81106c2cdc73b..0bf1ec7ece55d 100644 --- a/x-pack/plugins/actions/server/routes/legacy/update.ts +++ b/x-pack/plugins/actions/server/routes/legacy/update.ts @@ -37,6 +37,7 @@ export const updateActionRoute = ( options: { access: 'public', summary: `Update a connector`, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, tags: ['oas-tag:connectors'], }, diff --git a/x-pack/plugins/alerting/common/routes/backfill/response/schemas/v1.ts b/x-pack/plugins/alerting/common/routes/backfill/response/schemas/v1.ts index 268ef7f5e90d1..5da51d53dddbb 100644 --- a/x-pack/plugins/alerting/common/routes/backfill/response/schemas/v1.ts +++ b/x-pack/plugins/alerting/common/routes/backfill/response/schemas/v1.ts @@ -6,6 +6,7 @@ */ import { schema } from '@kbn/config-schema'; +import { ruleParamsSchemaV1 } from '@kbn/response-ops-rule-params'; import { adHocRunStatus } from '../../../../constants'; export const statusSchema = schema.oneOf([ @@ -26,7 +27,7 @@ export const backfillResponseSchema = schema.object({ name: schema.string(), tags: schema.arrayOf(schema.string()), rule_type_id: schema.string(), - params: schema.recordOf(schema.string(), schema.maybe(schema.any())), + params: ruleParamsSchemaV1, api_key_owner: schema.nullable(schema.string()), api_key_created_by_user: schema.maybe(schema.nullable(schema.boolean())), consumer: schema.string(), diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/create/schemas/v1.ts b/x-pack/plugins/alerting/common/routes/rule/apis/create/schemas/v1.ts index d9157850bfd8d..e70df7f9dc73f 100644 --- a/x-pack/plugins/alerting/common/routes/rule/apis/create/schemas/v1.ts +++ b/x-pack/plugins/alerting/common/routes/rule/apis/create/schemas/v1.ts @@ -6,6 +6,7 @@ */ import { schema } from '@kbn/config-schema'; +import { ruleParamsSchemaWithDefaultValueV1 } from '@kbn/response-ops-rule-params'; import { validateDurationV1, validateHoursV1, validateTimezoneV1 } from '../../../validation'; import { notifyWhenSchemaV1, alertDelaySchemaV1 } from '../../../response'; import { alertsFilterQuerySchemaV1 } from '../../../../alerts_filter_query'; @@ -166,10 +167,7 @@ export const createBodySchema = schema.object({ }) ) ), - params: schema.recordOf(schema.string(), schema.maybe(schema.any()), { - defaultValue: {}, - meta: { description: 'The parameters for the rule.' }, - }), + params: ruleParamsSchemaWithDefaultValueV1, schedule: schema.object( { interval: schema.string({ diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/update/schemas/v1.ts b/x-pack/plugins/alerting/common/routes/rule/apis/update/schemas/v1.ts index e83e26f119595..b838d21e5cc03 100644 --- a/x-pack/plugins/alerting/common/routes/rule/apis/update/schemas/v1.ts +++ b/x-pack/plugins/alerting/common/routes/rule/apis/update/schemas/v1.ts @@ -6,6 +6,7 @@ */ import { schema } from '@kbn/config-schema'; +import { ruleParamsSchemaWithDefaultValueV1 } from '@kbn/response-ops-rule-params'; import { validateDurationV1, validateHoursV1, validateTimezoneV1 } from '../../../validation'; import { notifyWhenSchemaV1, alertDelaySchemaV1 } from '../../../response'; import { alertsFilterQuerySchemaV1 } from '../../../../alerts_filter_query'; @@ -152,10 +153,7 @@ export const updateBodySchema = schema.object({ }) ) ), - params: schema.recordOf(schema.string(), schema.any(), { - defaultValue: {}, - meta: { description: 'The parameters for the rule.' }, - }), + params: ruleParamsSchemaWithDefaultValueV1, actions: schema.arrayOf(actionSchema, { defaultValue: [] }), notify_when: schema.maybe(schema.nullable(notifyWhenSchemaV1)), alert_delay: schema.maybe(alertDelaySchemaV1), diff --git a/x-pack/plugins/alerting/common/routes/rule/response/index.ts b/x-pack/plugins/alerting/common/routes/rule/response/index.ts index 8c784e744d473..1c7632ad28988 100644 --- a/x-pack/plugins/alerting/common/routes/rule/response/index.ts +++ b/x-pack/plugins/alerting/common/routes/rule/response/index.ts @@ -6,7 +6,6 @@ */ export { - ruleParamsSchema, actionParamsSchema, mappedParamsSchema, ruleExecutionStatusSchema, @@ -18,16 +17,9 @@ export { scheduleIdsSchema, } from './schemas/latest'; -export type { - RuleParams, - RuleResponse, - RuleSnoozeSchedule, - RuleLastRun, - Monitoring, -} from './types/latest'; +export type { RuleResponse, RuleSnoozeSchedule, RuleLastRun, Monitoring } from './types/latest'; export { - ruleParamsSchema as ruleParamsSchemaV1, actionParamsSchema as actionParamsSchemaV1, mappedParamsSchema as mappedParamsSchemaV1, ruleExecutionStatusSchema as ruleExecutionStatusSchemaV1, @@ -41,9 +33,14 @@ export { } from './schemas/v1'; export type { - RuleParams as RuleParamsV1, RuleResponse as RuleResponseV1, RuleSnoozeSchedule as RuleSnoozeScheduleV1, RuleLastRun as RuleLastRunV1, Monitoring as MonitoringV1, } from './types/v1'; + +export { ruleParamsSchemaV1 } from '@kbn/response-ops-rule-params'; +export { ruleParamsSchema } from '@kbn/response-ops-rule-params'; + +export type { RuleParamsV1 } from '@kbn/response-ops-rule-params'; +export type { RuleParams } from '@kbn/response-ops-rule-params'; diff --git a/x-pack/plugins/alerting/common/routes/rule/response/schemas/v1.ts b/x-pack/plugins/alerting/common/routes/rule/response/schemas/v1.ts index 29488b98d6ca8..069aca001d14f 100644 --- a/x-pack/plugins/alerting/common/routes/rule/response/schemas/v1.ts +++ b/x-pack/plugins/alerting/common/routes/rule/response/schemas/v1.ts @@ -6,6 +6,7 @@ */ import { schema } from '@kbn/config-schema'; +import { ruleParamsSchemaV1 } from '@kbn/response-ops-rule-params'; import { rRuleResponseSchemaV1 } from '../../../r_rule'; import { alertsFilterQuerySchemaV1 } from '../../../alerts_filter_query'; import { @@ -18,9 +19,6 @@ import { import { validateNotifyWhenV1 } from '../../validation'; import { flappingSchemaV1 } from '../../common'; -export const ruleParamsSchema = schema.recordOf(schema.string(), schema.maybe(schema.any()), { - meta: { description: 'The parameters for the rule.' }, -}); export const actionParamsSchema = schema.recordOf(schema.string(), schema.maybe(schema.any()), { meta: { description: @@ -497,7 +495,7 @@ export const ruleResponseSchema = schema.object({ }), schedule: intervalScheduleSchema, actions: schema.arrayOf(actionSchema), - params: ruleParamsSchema, + params: ruleParamsSchemaV1, mapped_params: schema.maybe(mappedParamsSchema), scheduled_task_id: schema.maybe( schema.string({ diff --git a/x-pack/plugins/alerting/common/routes/rule/response/types/v1.ts b/x-pack/plugins/alerting/common/routes/rule/response/types/v1.ts index e9a37eea1fe72..e32a56a302e63 100644 --- a/x-pack/plugins/alerting/common/routes/rule/response/types/v1.ts +++ b/x-pack/plugins/alerting/common/routes/rule/response/types/v1.ts @@ -6,22 +6,21 @@ */ import type { TypeOf } from '@kbn/config-schema'; +import { RuleParamsV1 } from '@kbn/response-ops-rule-params'; import { - ruleParamsSchemaV1, ruleResponseSchemaV1, ruleSnoozeScheduleSchemaV1, ruleLastRunSchemaV1, monitoringSchemaV1, } from '..'; -export type RuleParams = TypeOf; export type RuleSnoozeSchedule = TypeOf; export type RuleLastRun = TypeOf; export type Monitoring = TypeOf; type RuleResponseSchemaType = TypeOf; -export interface RuleResponse { +export interface RuleResponse { id: RuleResponseSchemaType['id']; enabled: RuleResponseSchemaType['enabled']; name: RuleResponseSchemaType['name']; diff --git a/x-pack/plugins/alerting/server/application/backfill/result/schemas/index.ts b/x-pack/plugins/alerting/server/application/backfill/result/schemas/index.ts index de3cc5926a4ae..b454d41dd40ca 100644 --- a/x-pack/plugins/alerting/server/application/backfill/result/schemas/index.ts +++ b/x-pack/plugins/alerting/server/application/backfill/result/schemas/index.ts @@ -6,6 +6,7 @@ */ import { schema } from '@kbn/config-schema'; +import { ruleParamsSchema } from '@kbn/response-ops-rule-params'; import { adHocRunStatus } from '../../../../../common/constants'; export const statusSchema = schema.oneOf([ @@ -32,7 +33,7 @@ export const backfillSchema = schema.object({ name: schema.string(), tags: schema.arrayOf(schema.string()), alertTypeId: schema.string(), - params: schema.recordOf(schema.string(), schema.maybe(schema.any())), + params: ruleParamsSchema, apiKeyOwner: schema.nullable(schema.string()), apiKeyCreatedByUser: schema.maybe(schema.nullable(schema.boolean())), consumer: schema.string(), diff --git a/x-pack/plugins/alerting/server/application/rule/methods/create/schemas/create_rule_data_schema.ts b/x-pack/plugins/alerting/server/application/rule/methods/create/schemas/create_rule_data_schema.ts index 0672d7929fdb2..e2cf0da359b0a 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/create/schemas/create_rule_data_schema.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/create/schemas/create_rule_data_schema.ts @@ -6,6 +6,7 @@ */ import { schema } from '@kbn/config-schema'; +import { ruleParamsSchemaWithDefaultValue } from '@kbn/response-ops-rule-params'; import { validateDuration } from '../../../validation'; import { notifyWhenSchema, @@ -23,7 +24,7 @@ export const createRuleDataSchema = schema.object( consumer: schema.string(), tags: schema.arrayOf(schema.string(), { defaultValue: [] }), throttle: schema.maybe(schema.nullable(schema.string({ validate: validateDuration }))), - params: schema.recordOf(schema.string(), schema.maybe(schema.any()), { defaultValue: {} }), + params: ruleParamsSchemaWithDefaultValue, schedule: schema.object({ interval: schema.string({ validate: validateDuration }), }), diff --git a/x-pack/plugins/alerting/server/application/rule/methods/update/schemas/update_rule_data_schema.ts b/x-pack/plugins/alerting/server/application/rule/methods/update/schemas/update_rule_data_schema.ts index 0c4a1df45d44e..9c0bf1666f846 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/update/schemas/update_rule_data_schema.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/update/schemas/update_rule_data_schema.ts @@ -6,6 +6,7 @@ */ import { schema } from '@kbn/config-schema'; +import { ruleParamsSchemaWithDefaultValue } from '@kbn/response-ops-rule-params'; import { validateDuration } from '../../../validation'; import { notifyWhenSchema, @@ -23,7 +24,7 @@ export const updateRuleDataSchema = schema.object( interval: schema.string({ validate: validateDuration }), }), throttle: schema.maybe(schema.nullable(schema.string({ validate: validateDuration }))), - params: schema.recordOf(schema.string(), schema.maybe(schema.any()), { defaultValue: {} }), + params: ruleParamsSchemaWithDefaultValue, actions: schema.arrayOf(actionRequestSchema, { defaultValue: [] }), systemActions: schema.maybe(schema.arrayOf(systemActionRequestSchema, { defaultValue: [] })), notifyWhen: schema.maybe(schema.nullable(notifyWhenSchema)), diff --git a/x-pack/plugins/alerting/server/application/rule/schemas/rule_schemas.ts b/x-pack/plugins/alerting/server/application/rule/schemas/rule_schemas.ts index 7f9fcb1bd5377..da91ceb727d2c 100644 --- a/x-pack/plugins/alerting/server/application/rule/schemas/rule_schemas.ts +++ b/x-pack/plugins/alerting/server/application/rule/schemas/rule_schemas.ts @@ -6,6 +6,7 @@ */ import { schema } from '@kbn/config-schema'; +import { ruleParamsSchema } from '@kbn/response-ops-rule-params'; import { ruleLastRunOutcomeValues, ruleExecutionStatusValues, @@ -18,7 +19,6 @@ import { notifyWhenSchema } from './notify_when_schema'; import { actionSchema, systemActionSchema } from './action_schemas'; import { flappingSchema } from './flapping_schema'; -export const ruleParamsSchema = schema.recordOf(schema.string(), schema.maybe(schema.any())); export const mappedParamsSchema = schema.recordOf(schema.string(), schema.maybe(schema.any())); export const intervalScheduleSchema = schema.object({ diff --git a/x-pack/plugins/alerting/server/application/rule/types/rule.ts b/x-pack/plugins/alerting/server/application/rule/types/rule.ts index 0b1177d31e1f7..2e5cad45cf92f 100644 --- a/x-pack/plugins/alerting/server/application/rule/types/rule.ts +++ b/x-pack/plugins/alerting/server/application/rule/types/rule.ts @@ -6,6 +6,7 @@ */ import { TypeOf } from '@kbn/config-schema'; +import { ruleParamsSchema } from '@kbn/response-ops-rule-params'; import { ruleNotifyWhen, ruleLastRunOutcomeValues, @@ -14,7 +15,6 @@ import { ruleExecutionStatusWarningReason, } from '../constants'; import { - ruleParamsSchema, snoozeScheduleSchema, ruleExecutionStatusSchema, ruleLastRunSchema, diff --git a/x-pack/plugins/alerting/server/lib/retry_if_conflicts.ts b/x-pack/plugins/alerting/server/lib/retry_if_conflicts.ts index 8b7a3b5f76c8a..0f9377e83ee6c 100644 --- a/x-pack/plugins/alerting/server/lib/retry_if_conflicts.ts +++ b/x-pack/plugins/alerting/server/lib/retry_if_conflicts.ts @@ -22,7 +22,7 @@ export const RetryForConflictsAttempts = 2; // note: we considered making this random, to help avoid a stampede, but // with 1 retry it probably doesn't matter, and adding randomness could // make it harder to diagnose issues -const RetryForConflictsDelay = 250; +const RetryForConflictsDelay = 100; // retry an operation if it runs into 409 Conflict's, up to a limit export async function retryIfConflicts( diff --git a/x-pack/plugins/alerting/server/routes/index.ts b/x-pack/plugins/alerting/server/routes/index.ts index 97fdf8c90f8d6..1a274692cefe4 100644 --- a/x-pack/plugins/alerting/server/routes/index.ts +++ b/x-pack/plugins/alerting/server/routes/index.ts @@ -20,7 +20,8 @@ import { deleteRuleRoute } from './rule/apis/delete/delete_rule_route'; import { aggregateRulesRoute } from './rule/apis/aggregate/aggregate_rules_route'; import { disableRuleRoute } from './rule/apis/disable/disable_rule_route'; import { enableRuleRoute } from './rule/apis/enable/enable_rule_route'; -import { findRulesRoute, findInternalRulesRoute } from './rule/apis/find/find_rules_route'; +import { findRulesRoute } from './rule/apis/find/find_rules_route'; +import { findInternalRulesRoute } from './rule/apis/find/find_internal_rules_route'; import { getRuleAlertSummaryRoute } from './get_rule_alert_summary'; import { getRuleExecutionLogRoute } from './get_rule_execution_log'; import { getGlobalExecutionLogRoute } from './get_global_execution_logs'; diff --git a/x-pack/plugins/alerting/server/routes/legacy/create.ts b/x-pack/plugins/alerting/server/routes/legacy/create.ts index d8505fdc8453d..333877b7df49e 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/create.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/create.ts @@ -65,6 +65,7 @@ export const createAlertRoute = ({ access: isServerless ? 'internal' : 'public', summary: 'Create an alert', tags: ['oas-tag:alerting'], + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, }, diff --git a/x-pack/plugins/alerting/server/routes/legacy/delete.ts b/x-pack/plugins/alerting/server/routes/legacy/delete.ts index f931af10ccbbf..2b63de9e4ee73 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/delete.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/delete.ts @@ -33,6 +33,7 @@ export const deleteAlertRoute = ( access: isServerless ? 'internal' : 'public', summary: 'Delete an alert', tags: ['oas-tag:alerting'], + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, }, diff --git a/x-pack/plugins/alerting/server/routes/legacy/disable.ts b/x-pack/plugins/alerting/server/routes/legacy/disable.ts index 486bef89dd197..0c6f3cf062a0c 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/disable.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/disable.ts @@ -34,6 +34,7 @@ export const disableAlertRoute = ( access: isServerless ? 'internal' : 'public', summary: 'Disable an alert', tags: ['oas-tag:alerting'], + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, }, diff --git a/x-pack/plugins/alerting/server/routes/legacy/enable.ts b/x-pack/plugins/alerting/server/routes/legacy/enable.ts index c5076b3de1a54..d52eaa784f670 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/enable.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/enable.ts @@ -35,6 +35,7 @@ export const enableAlertRoute = ( access: isServerless ? 'internal' : 'public', summary: 'Enable an alert', tags: ['oas-tag:alerting'], + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, }, diff --git a/x-pack/plugins/alerting/server/routes/legacy/find.ts b/x-pack/plugins/alerting/server/routes/legacy/find.ts index ad85f3c7333b0..fa309ae51f2e4 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/find.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/find.ts @@ -79,6 +79,7 @@ export const findAlertRoute = ( tags: ['oas-tag:alerting'], description: 'Gets a paginated set of alerts. Alert `params` are stored as a flattened field type and analyzed as keywords. As alerts change in Kibana, the results on each page of the response also change. Use the find API for traditional paginated results, but avoid using it to export large amounts of data.', + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, }, diff --git a/x-pack/plugins/alerting/server/routes/legacy/get.ts b/x-pack/plugins/alerting/server/routes/legacy/get.ts index 8437b888f7c0f..e5eff52bf02d6 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/get.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/get.ts @@ -33,6 +33,7 @@ export const getAlertRoute = ( access: isServerless ? 'internal' : 'public', summary: 'Get an alert', tags: ['oas-tag:alerting'], + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, }, diff --git a/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.ts b/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.ts index fd1fae64b538f..58a75dd68dce7 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.ts @@ -44,6 +44,7 @@ export const getAlertInstanceSummaryRoute = ( access: isServerless ? 'internal' : 'public', summary: 'Get an alert summary', tags: ['oas-tag:alerting'], + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, }, diff --git a/x-pack/plugins/alerting/server/routes/legacy/get_alert_state.ts b/x-pack/plugins/alerting/server/routes/legacy/get_alert_state.ts index 5943ab7203599..e952ef8719667 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/get_alert_state.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/get_alert_state.ts @@ -33,6 +33,7 @@ export const getAlertStateRoute = ( access: isServerless ? 'internal' : 'public', summary: 'Get the state of an alert', tags: ['oas-tag:alerting'], + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, }, diff --git a/x-pack/plugins/alerting/server/routes/legacy/health.ts b/x-pack/plugins/alerting/server/routes/legacy/health.ts index 90bfda371932a..8f67767941fd2 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/health.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/health.ts @@ -29,6 +29,7 @@ export function healthRoute( access: isServerless ? 'internal' : 'public', summary: 'Get the alerting framework health', tags: ['oas-tag:alerting'], + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, }, diff --git a/x-pack/plugins/alerting/server/routes/legacy/list_alert_types.ts b/x-pack/plugins/alerting/server/routes/legacy/list_alert_types.ts index 6668ff219ade0..35d6a7efeeee3 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/list_alert_types.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/list_alert_types.ts @@ -25,6 +25,7 @@ export const listAlertTypesRoute = ( access: isServerless ? 'internal' : 'public', summary: 'Get the alert types', tags: ['oas-tag:alerting'], + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, }, diff --git a/x-pack/plugins/alerting/server/routes/legacy/mute_all.ts b/x-pack/plugins/alerting/server/routes/legacy/mute_all.ts index eaa989dc8fb6a..5c4fc1542ef5b 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/mute_all.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/mute_all.ts @@ -34,6 +34,7 @@ export const muteAllAlertRoute = ( access: isServerless ? 'internal' : 'public', summary: 'Mute all alert instances', tags: ['oas-tag:alerting'], + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, }, diff --git a/x-pack/plugins/alerting/server/routes/legacy/mute_instance.ts b/x-pack/plugins/alerting/server/routes/legacy/mute_instance.ts index c309dd36b7744..ab0b52d41de29 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/mute_instance.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/mute_instance.ts @@ -37,6 +37,7 @@ export const muteAlertInstanceRoute = ( access: isServerless ? 'internal' : 'public', summary: 'Mute an alert', tags: ['oas-tag:alerting'], + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, }, diff --git a/x-pack/plugins/alerting/server/routes/legacy/unmute_all.ts b/x-pack/plugins/alerting/server/routes/legacy/unmute_all.ts index 70dfd65e33c79..0681e7d2cf01e 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/unmute_all.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/unmute_all.ts @@ -34,6 +34,7 @@ export const unmuteAllAlertRoute = ( access: isServerless ? 'internal' : 'public', summary: 'Unmute all alert instances', tags: ['oas-tag:alerting'], + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, }, diff --git a/x-pack/plugins/alerting/server/routes/legacy/unmute_instance.ts b/x-pack/plugins/alerting/server/routes/legacy/unmute_instance.ts index 7990539d6c20d..1101a2b5092e7 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/unmute_instance.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/unmute_instance.ts @@ -35,6 +35,7 @@ export const unmuteAlertInstanceRoute = ( access: isServerless ? 'internal' : 'public', summary: 'Unmute an alert', tags: ['oas-tag:alerting'], + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, }, diff --git a/x-pack/plugins/alerting/server/routes/legacy/update.ts b/x-pack/plugins/alerting/server/routes/legacy/update.ts index b65579e17b087..01adeb5c634dc 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/update.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/update.ts @@ -61,6 +61,7 @@ export const updateAlertRoute = ( access: isServerless ? 'internal' : 'public', summary: 'Update an alert', tags: ['oas-tag:alerting'], + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, }, diff --git a/x-pack/plugins/alerting/server/routes/legacy/update_api_key.ts b/x-pack/plugins/alerting/server/routes/legacy/update_api_key.ts index 06c466333967c..30c51d3cdcf5c 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/update_api_key.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/update_api_key.ts @@ -35,6 +35,7 @@ export const updateApiKeyRoute = ( access: isServerless ? 'internal' : 'public', summary: 'Update the API key for an alert', tags: ['oas-tag:alerting'], + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, }, diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/find/find_internal_rules_route.test.ts b/x-pack/plugins/alerting/server/routes/rule/apis/find/find_internal_rules_route.test.ts new file mode 100644 index 0000000000000..46ff2e8e96e12 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/rule/apis/find/find_internal_rules_route.test.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 { httpServiceMock } from '@kbn/core/server/mocks'; +import { licenseStateMock } from '../../../../lib/license_state.mock'; +import { findInternalRulesRoute } from './find_internal_rules_route'; + +jest.mock('../../../../lib/license_api_access', () => ({ + verifyApiAccess: jest.fn(), +})); + +jest.mock('../../../lib/track_legacy_terminology', () => ({ + trackLegacyTerminology: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('findInternalRulesRoute', () => { + it('registers the route without public access', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + findInternalRulesRoute(router, licenseState); + expect(router.post).toHaveBeenCalledWith( + expect.not.objectContaining({ + options: expect.objectContaining({ access: 'public' }), + }), + expect.any(Function) + ); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/find/find_internal_rules_route.ts b/x-pack/plugins/alerting/server/routes/rule/apis/find/find_internal_rules_route.ts new file mode 100644 index 0000000000000..8ff8ce59192cf --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/rule/apis/find/find_internal_rules_route.ts @@ -0,0 +1,87 @@ +/* + * Copyright 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 { IRouter } from '@kbn/core/server'; +import { UsageCounter } from '@kbn/usage-collection-plugin/server'; +import type { + FindRulesRequestQueryV1, + FindRulesResponseV1, +} from '../../../../../common/routes/rule/apis/find'; +import { findRulesRequestQuerySchemaV1 } from '../../../../../common/routes/rule/apis/find'; +import { RuleParamsV1 } from '../../../../../common/routes/rule/response'; +import { ILicenseState } from '../../../../lib'; +import { + AlertingRequestHandlerContext, + INTERNAL_ALERTING_API_FIND_RULES_PATH, +} from '../../../../types'; +import { verifyAccessAndContext } from '../../../lib'; +import { trackLegacyTerminology } from '../../../lib/track_legacy_terminology'; +import { transformFindRulesBodyV1, transformFindRulesResponseV1 } from './transforms'; + +export const findInternalRulesRoute = ( + router: IRouter, + licenseState: ILicenseState, + usageCounter?: UsageCounter +) => { + router.post( + { + path: INTERNAL_ALERTING_API_FIND_RULES_PATH, + options: { access: 'internal' }, + validate: { + body: findRulesRequestQuerySchemaV1, + }, + }, + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const rulesClient = (await context.alerting).getRulesClient(); + + const body: FindRulesRequestQueryV1 = req.body; + + trackLegacyTerminology( + [req.body.search, req.body.search_fields, req.body.sort_field].filter( + Boolean + ) as string[], + usageCounter + ); + + const options = transformFindRulesBodyV1({ + ...body, + has_reference: body.has_reference || undefined, + search_fields: searchFieldsAsArray(body.search_fields), + }); + + if (req.body.fields) { + usageCounter?.incrementCounter({ + counterName: `alertingFieldsUsage`, + counterType: 'alertingFieldsUsage', + incrementBy: 1, + }); + } + + const findResult = await rulesClient.find({ + options, + excludeFromPublicApi: false, + includeSnoozeData: true, + }); + + const responseBody: FindRulesResponseV1['body'] = + transformFindRulesResponseV1(findResult, options.fields); + + return res.ok({ + body: responseBody, + }); + }) + ) + ); +}; + +function searchFieldsAsArray(searchFields: string | string[] | undefined): string[] | undefined { + if (!searchFields) { + return; + } + return Array.isArray(searchFields) ? searchFields : [searchFields]; +} diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/find/find_rules_route.test.ts b/x-pack/plugins/alerting/server/routes/rule/apis/find/find_rules_route.test.ts index 4e3c9b635ec3a..0e1f07a5ce543 100644 --- a/x-pack/plugins/alerting/server/routes/rule/apis/find/find_rules_route.test.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/find/find_rules_route.test.ts @@ -30,6 +30,18 @@ beforeEach(() => { }); describe('findRulesRoute', () => { + it('registers the route with public access', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + findRulesRoute(router, licenseState); + expect(router.get).toHaveBeenCalledWith( + expect.objectContaining({ + options: expect.objectContaining({ access: 'public' }), + }), + expect.any(Function) + ); + }); it('finds rules with proper parameters', async () => { const licenseState = licenseStateMock.create(); const router = httpServiceMock.createRouter(); diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/find/find_rules_route.ts b/x-pack/plugins/alerting/server/routes/rule/apis/find/find_rules_route.ts index b4384e9d8f4ef..90afde8f20813 100644 --- a/x-pack/plugins/alerting/server/routes/rule/apis/find/find_rules_route.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/find/find_rules_route.ts @@ -7,40 +7,26 @@ import { IRouter } from '@kbn/core/server'; import { UsageCounter } from '@kbn/usage-collection-plugin/server'; -import { ILicenseState } from '../../../../lib'; -import { verifyAccessAndContext } from '../../../lib'; -import { findRulesRequestQuerySchemaV1 } from '../../../../../common/routes/rule/apis/find'; import type { FindRulesRequestQueryV1, FindRulesResponseV1, } from '../../../../../common/routes/rule/apis/find'; +import { findRulesRequestQuerySchemaV1 } from '../../../../../common/routes/rule/apis/find'; import { RuleParamsV1, ruleResponseSchemaV1 } from '../../../../../common/routes/rule/response'; -import { - AlertingRequestHandlerContext, - BASE_ALERTING_API_PATH, - INTERNAL_ALERTING_API_FIND_RULES_PATH, -} from '../../../../types'; +import { ILicenseState } from '../../../../lib'; +import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../../../../types'; +import { verifyAccessAndContext } from '../../../lib'; import { trackLegacyTerminology } from '../../../lib/track_legacy_terminology'; import { transformFindRulesBodyV1, transformFindRulesResponseV1 } from './transforms'; -interface BuildFindRulesRouteParams { - licenseState: ILicenseState; - path: string; - router: IRouter; - excludeFromPublicApi?: boolean; - usageCounter?: UsageCounter; -} - -const buildFindRulesRoute = ({ - licenseState, - path, - router, - excludeFromPublicApi = false, - usageCounter, -}: BuildFindRulesRouteParams) => { +export const findRulesRoute = ( + router: IRouter, + licenseState: ILicenseState, + usageCounter?: UsageCounter +) => { router.get( { - path, + path: `${BASE_ALERTING_API_PATH}/rules/_find`, options: { access: 'public', summary: 'Get information about rules', @@ -91,7 +77,7 @@ const buildFindRulesRoute = ({ const findResult = await rulesClient.find({ options, - excludeFromPublicApi, + excludeFromPublicApi: true, includeSnoozeData: true, }); @@ -104,86 +90,6 @@ const buildFindRulesRoute = ({ }) ) ); - if (path === INTERNAL_ALERTING_API_FIND_RULES_PATH) { - router.post( - { - path, - options: { access: 'internal' }, - validate: { - body: findRulesRequestQuerySchemaV1, - }, - }, - router.handleLegacyErrors( - verifyAccessAndContext(licenseState, async function (context, req, res) { - const rulesClient = (await context.alerting).getRulesClient(); - - const body: FindRulesRequestQueryV1 = req.body; - - trackLegacyTerminology( - [req.body.search, req.body.search_fields, req.body.sort_field].filter( - Boolean - ) as string[], - usageCounter - ); - - const options = transformFindRulesBodyV1({ - ...body, - has_reference: body.has_reference || undefined, - search_fields: searchFieldsAsArray(body.search_fields), - }); - - if (req.body.fields) { - usageCounter?.incrementCounter({ - counterName: `alertingFieldsUsage`, - counterType: 'alertingFieldsUsage', - incrementBy: 1, - }); - } - - const findResult = await rulesClient.find({ - options, - excludeFromPublicApi, - includeSnoozeData: true, - }); - - const responseBody: FindRulesResponseV1['body'] = - transformFindRulesResponseV1(findResult, options.fields); - - return res.ok({ - body: responseBody, - }); - }) - ) - ); - } -}; - -export const findRulesRoute = ( - router: IRouter, - licenseState: ILicenseState, - usageCounter?: UsageCounter -) => { - buildFindRulesRoute({ - excludeFromPublicApi: true, - licenseState, - path: `${BASE_ALERTING_API_PATH}/rules/_find`, - router, - usageCounter, - }); -}; - -export const findInternalRulesRoute = ( - router: IRouter, - licenseState: ILicenseState, - usageCounter?: UsageCounter -) => { - buildFindRulesRoute({ - excludeFromPublicApi: false, - licenseState, - path: INTERNAL_ALERTING_API_FIND_RULES_PATH, - router, - usageCounter, - }); }; function searchFieldsAsArray(searchFields: string | string[] | undefined): string[] | undefined { diff --git a/x-pack/plugins/alerting/tsconfig.json b/x-pack/plugins/alerting/tsconfig.json index c0951663a8489..eefc1999b26d5 100644 --- a/x-pack/plugins/alerting/tsconfig.json +++ b/x-pack/plugins/alerting/tsconfig.json @@ -73,7 +73,8 @@ "@kbn/core-security-server", "@kbn/core-http-server", "@kbn/zod", - "@kbn/core-saved-objects-base-server-internal" + "@kbn/core-saved-objects-base-server-internal", + "@kbn/response-ops-rule-params" ], "exclude": [ "target/**/*" diff --git a/x-pack/plugins/cases/public/common/mock/test_providers.tsx b/x-pack/plugins/cases/public/common/mock/test_providers.tsx index aef1024cc8afc..257ac4b1f8293 100644 --- a/x-pack/plugins/cases/public/common/mock/test_providers.tsx +++ b/x-pack/plugins/cases/public/common/mock/test_providers.tsx @@ -89,22 +89,20 @@ const TestProvidersComponent: React.FC = ({ }); const getFilesClient = mockGetFilesClient(); + const casesProviderValue = { + externalReferenceAttachmentTypeRegistry, + persistableStateAttachmentTypeRegistry, + features, + owner, + permissions, + getFilesClient, + }; return ( - + {children} @@ -170,23 +168,20 @@ export const createAppMockRenderer = ({ }); const getFilesClient = mockGetFilesClient(); - + const casesProviderValue = { + externalReferenceAttachmentTypeRegistry, + persistableStateAttachmentTypeRegistry, + features, + owner, + permissions, + releasePhase, + getFilesClient, + }; const AppWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => ( - + {children} @@ -195,10 +190,11 @@ export const createAppMockRenderer = ({ ); AppWrapper.displayName = 'AppWrapper'; + const memoizedAppWrapper = React.memo(AppWrapper); const render: UiRender = (ui, options) => { return reactRender(ui, { - wrapper: AppWrapper, + wrapper: memoizedAppWrapper, ...options, }); }; diff --git a/x-pack/plugins/cases/public/components/all_cases/utility_bar.test.tsx b/x-pack/plugins/cases/public/components/all_cases/utility_bar.test.tsx index bd72a460c8c71..63d8cd2e5faab 100644 --- a/x-pack/plugins/cases/public/components/all_cases/utility_bar.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/utility_bar.test.tsx @@ -41,7 +41,17 @@ describe('Severity form field', () => { showClearFiltersButton: false, }; + beforeAll(() => { + jest.useFakeTimers(); + }); + + afterAll(() => { + jest.useRealTimers(); + sessionStorage.removeItem(localStorageKey); + }); + beforeEach(() => { + jest.useFakeTimers(); // Workaround for timeout via https://github.com/testing-library/user-event/issues/833#issuecomment-1171452841 user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime, diff --git a/x-pack/plugins/cases/public/components/files/file_preview.test.tsx b/x-pack/plugins/cases/public/components/files/file_preview.test.tsx index 0f4edf9201d5e..33288f061e25f 100644 --- a/x-pack/plugins/cases/public/components/files/file_preview.test.tsx +++ b/x-pack/plugins/cases/public/components/files/file_preview.test.tsx @@ -16,7 +16,8 @@ import { createAppMockRenderer, mockedTestProvidersOwner } from '../../common/mo import { basicFileMock } from '../../containers/mock'; import { FilePreview } from './file_preview'; -describe('FilePreview', () => { +// FLAKY: https://github.com/elastic/kibana/issues/182364 +describe.skip('FilePreview', () => { let user: UserEvent; let appMockRender: AppMockRenderer; diff --git a/x-pack/plugins/cases/public/components/links/index.test.tsx b/x-pack/plugins/cases/public/components/links/index.test.tsx index 5c450edc39d8a..94d8776af1d92 100644 --- a/x-pack/plugins/cases/public/components/links/index.test.tsx +++ b/x-pack/plugins/cases/public/components/links/index.test.tsx @@ -16,7 +16,8 @@ import { useCaseViewNavigation } from '../../common/navigation/hooks'; jest.mock('../../common/navigation/hooks'); -describe('Configuration button', () => { +// FLAKY: https://github.com/elastic/kibana/issues/196189 +describe.skip('Configuration button', () => { const props: ConfigureCaseButtonProps = { label: 'My label', msgTooltip: <>, diff --git a/x-pack/plugins/cases/public/components/system_actions/cases/cases.tsx b/x-pack/plugins/cases/public/components/system_actions/cases/cases.tsx index 0506b2069a3c4..e00c4bd2d5ccd 100644 --- a/x-pack/plugins/cases/public/components/system_actions/cases/cases.tsx +++ b/x-pack/plugins/cases/public/components/system_actions/cases/cases.tsx @@ -30,7 +30,7 @@ export function getConnectorType(): ConnectorTypeModel<{}, {}, CasesActionParams selectMessage: i18n.CASE_ACTION_DESC, actionTypeTitle: CASES_CONNECTOR_TITLE, actionConnectorFields: null, - isExperimental: true, + isExperimental: false, validateParams: async ( actionParams: CasesActionParams ): Promise> => { diff --git a/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts b/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts index a33b638f5310e..6e8ac79bffec9 100644 --- a/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts +++ b/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts @@ -31,6 +31,7 @@ export const getAllCommentsRoute = createCasesRoute({ summary: `Gets all case comments`, tags: ['oas-tag:cases'], // description: 'You must have `read` privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the cases with the comments you\'re seeking.', + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, handler: async ({ context, request, response }) => { diff --git a/x-pack/plugins/cases/server/routes/api/stats/get_status.ts b/x-pack/plugins/cases/server/routes/api/stats/get_status.ts index 07d02c0b6713f..dce369e4a0f45 100644 --- a/x-pack/plugins/cases/server/routes/api/stats/get_status.ts +++ b/x-pack/plugins/cases/server/routes/api/stats/get_status.ts @@ -26,6 +26,7 @@ export const getStatusRoute: CaseRoute = createCasesRoute({ description: 'Returns the number of cases that are open, closed, and in progress in the default space.', // You must have `read` privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the cases you're seeking. + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, handler: async ({ context, request, response }) => { diff --git a/x-pack/plugins/cases/server/routes/api/user_actions/get_all_user_actions.ts b/x-pack/plugins/cases/server/routes/api/user_actions/get_all_user_actions.ts index 5edc7a261b3c4..17fe0dcdb9012 100644 --- a/x-pack/plugins/cases/server/routes/api/user_actions/get_all_user_actions.ts +++ b/x-pack/plugins/cases/server/routes/api/user_actions/get_all_user_actions.ts @@ -30,6 +30,7 @@ export const getUserActionsRoute = createCasesRoute({ description: `Returns all user activity for a case.`, // You must have `read` privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the case you're seeking. tags: ['oas-tag:cases'], + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }, handler: async ({ context, request, response }) => { diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/combined_fields/semantic_text.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/combined_fields/semantic_text.tsx index 5ec9216f599c6..c1f56214e2ce1 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/combined_fields/semantic_text.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/combined_fields/semantic_text.tsx @@ -41,8 +41,8 @@ export const SemanticTextForm: FC = ({ addCombinedField, hasNameCollision const { services: { http }, } = useDataVisualizerKibana(); - const [inferenceServices, setInferenceServices] = useState([]); - const [selectedInference, setSelectedInference] = useState(); + const [inferenceEndpoints, setInferenceEndpoints] = useState([]); + const [selectedInferenceEndpoint, setSelectedInferenceEndpoint] = useState(); const [selectedFieldOption, setSelectedFieldOption] = useState(); const [renameToFieldOption, setRenameToFieldOption] = useState(''); const [fieldError, setFieldError] = useState(); @@ -61,17 +61,17 @@ export const SemanticTextForm: FC = ({ addCombinedField, hasNameCollision useEffect(() => { http - .fetch('/internal/data_visualizer/inference_services', { + .fetch('/internal/data_visualizer/inference_endpoints', { method: 'GET', version: '1', }) .then((response) => { - const inferenceServiceOptions = response.map((service) => ({ - value: service.inference_id, - text: service.inference_id, + const inferenceEndpointOptions = response.map((endpoint) => ({ + value: endpoint.inference_id, + text: endpoint.inference_id, })); - setInferenceServices(inferenceServiceOptions); - setSelectedInference(inferenceServiceOptions[0]?.value ?? undefined); + setInferenceEndpoints(inferenceEndpointOptions); + setSelectedInferenceEndpoint(inferenceEndpointOptions[0]?.value ?? undefined); }); }, [http]); @@ -88,7 +88,7 @@ export const SemanticTextForm: FC = ({ addCombinedField, hasNameCollision renameToFieldOption === '' || renameToFieldOption === undefined || selectedFieldOption === undefined || - selectedInference === undefined + selectedInferenceEndpoint === undefined ) { return; } @@ -103,7 +103,7 @@ export const SemanticTextForm: FC = ({ addCombinedField, hasNameCollision newMappings.properties![renameToFieldOption ?? selectedFieldOption] = { // @ts-ignore types are missing semantic_text type: 'semantic_text', - inference_id: selectedInference, + inference_id: selectedInferenceEndpoint, }; return newMappings; }, @@ -138,12 +138,12 @@ export const SemanticTextForm: FC = ({ addCombinedField, hasNameCollision const isInvalid = useMemo(() => { return ( - !selectedInference || + !selectedInferenceEndpoint || !selectedFieldOption || renameToFieldOption === '' || fieldError !== undefined ); - }, [selectedInference, selectedFieldOption, renameToFieldOption, fieldError]); + }, [selectedInferenceEndpoint, selectedFieldOption, renameToFieldOption, fieldError]); return ( <> @@ -185,13 +185,13 @@ export const SemanticTextForm: FC = ({ addCombinedField, hasNameCollision setSelectedInference(e.target.value)} + options={inferenceEndpoints} + value={selectedInferenceEndpoint} + onChange={(e) => setSelectedInferenceEndpoint(e.target.value)} /> diff --git a/x-pack/plugins/data_visualizer/server/routes.ts b/x-pack/plugins/data_visualizer/server/routes.ts index 05234fc5583ee..e04ba7521bfa4 100644 --- a/x-pack/plugins/data_visualizer/server/routes.ts +++ b/x-pack/plugins/data_visualizer/server/routes.ts @@ -67,9 +67,16 @@ export function routes(coreSetup: CoreSetup, logger: Logger) } ); + /** + * @apiGroup DataVisualizer + * + * @api {get} /internal/data_visualizer/inference_endpoints Returns a list of inference endpoints which are currently deployed + * @apiName inferenceEndpoints + * @apiDescription Returns a list of inference endpoints where the underlying model is currently deployed + */ router.versioned .get({ - path: '/internal/data_visualizer/inference_services', + path: '/internal/data_visualizer/inference_endpoints', access: 'internal', options: { tags: ['access:fileUpload:analyzeFile'], @@ -87,7 +94,15 @@ export function routes(coreSetup: CoreSetup, logger: Logger) inference_id: '_all', }); - return response.ok({ body: endpoints }); + const filteredInferenceEndpoints = endpoints.filter((endpoint) => { + return ( + endpoint.task_type === 'sparse_embedding' || endpoint.task_type === 'text_embedding' + // TODO: add this back in when the fix has made it into es in 8.16 + // && endpoint.service_settings.num_allocations > 0 + ); + }); + + return response.ok({ body: filteredInferenceEndpoints }); } catch (e) { return response.customError(wrapError(e)); } diff --git a/x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.test.ts b/x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.test.ts index f387e94e80990..b0bdc9b98a86d 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.test.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.test.ts @@ -7,7 +7,7 @@ import { Type } from '@kbn/config-schema'; import type { IRouter, RequestHandler, RequestHandlerContext, RouteConfig } from '@kbn/core/server'; -import { kibanaResponseFactory } from '@kbn/core/server'; +import { kibanaResponseFactory, ReservedPrivilegesSet } from '@kbn/core/server'; import { httpServerMock } from '@kbn/core/server/mocks'; import { routeDefinitionParamsMock } from './index.mock'; @@ -43,9 +43,14 @@ describe('Key rotation routes', () => { }); it('correctly defines route.', () => { + expect(routeConfig.security).toEqual({ + authz: { + requiredPrivileges: [ReservedPrivilegesSet.superuser], + }, + }); expect(routeConfig.options).toEqual({ access: 'public', - tags: ['access:rotateEncryptionKey', 'oas-tag:saved objects'], + tags: ['oas-tag:saved objects'], summary: `Rotate a key for encrypted saved objects`, description: `If a saved object cannot be decrypted using the primary encryption key, Kibana attempts to decrypt it using the specified decryption-only keys. In most of the cases this overhead is negligible, but if you're dealing with a large number of saved objects and experiencing performance issues, you may want to rotate the encryption key. NOTE: Bulk key rotation can consume a considerable amount of resources and hence only user with a superuser role can trigger it.`, @@ -96,7 +101,7 @@ describe('Key rotation routes', () => { expect(config.options).toEqual({ access: 'internal', - tags: ['access:rotateEncryptionKey', 'oas-tag:saved objects'], + tags: ['oas-tag:saved objects'], summary: `Rotate a key for encrypted saved objects`, description: `If a saved object cannot be decrypted using the primary encryption key, Kibana attempts to decrypt it using the specified decryption-only keys. In most of the cases this overhead is negligible, but if you're dealing with a large number of saved objects and experiencing performance issues, you may want to rotate the encryption key. NOTE: Bulk key rotation can consume a considerable amount of resources and hence only user with a superuser role can trigger it.`, diff --git a/x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.ts b/x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.ts index 272e74c3a69cb..46df83a187c3b 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.ts @@ -6,6 +6,7 @@ */ import { schema } from '@kbn/config-schema'; +import { ReservedPrivilegesSet } from '@kbn/core/server'; import type { RouteDefinitionParams } from '.'; @@ -39,9 +40,14 @@ export function defineKeyRotationRoutes({ type: schema.maybe(schema.string()), }), }, + security: { + authz: { + requiredPrivileges: [ReservedPrivilegesSet.superuser], + }, + }, options: { - tags: ['access:rotateEncryptionKey', 'oas-tag:saved objects'], access: buildFlavor === 'serverless' ? 'internal' : 'public', + tags: ['oas-tag:saved objects'], summary: `Rotate a key for encrypted saved objects`, description: `If a saved object cannot be decrypted using the primary encryption key, Kibana attempts to decrypt it using the specified decryption-only keys. In most of the cases this overhead is negligible, but if you're dealing with a large number of saved objects and experiencing performance issues, you may want to rotate the encryption key. NOTE: Bulk key rotation can consume a considerable amount of resources and hence only user with a superuser role can trigger it.`, diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/generate_api_key_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/generate_api_key_panel.tsx index f1470f37bc8f0..52da11d51c074 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/generate_api_key_panel.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/generate_api_key_panel.tsx @@ -7,27 +7,20 @@ import React from 'react'; -import { useActions, useValues } from 'kea'; +import { useValues } from 'kea'; import { EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { GenerateApiKeyModal } from './components/generate_api_key_modal/modal'; - import { APIGettingStarted } from './components/getting_started/getting_started'; import { IndexViewLogic } from './index_view_logic'; -import { OverviewLogic } from './overview.logic'; export const GenerateApiKeyPanel: React.FC = () => { - const { isGenerateModalOpen } = useValues(OverviewLogic); - const { indexName, isHiddenIndex } = useValues(IndexViewLogic); - const { closeGenerateModal } = useActions(OverviewLogic); + const { isHiddenIndex } = useValues(IndexViewLogic); + return ( <> - {isGenerateModalOpen && ( - - )} diff --git a/x-pack/plugins/enterprise_search/server/integrations.ts b/x-pack/plugins/enterprise_search/server/integrations.ts index 2918ef862dbdf..d66768e578732 100644 --- a/x-pack/plugins/enterprise_search/server/integrations.ts +++ b/x-pack/plugins/enterprise_search/server/integrations.ts @@ -6,17 +6,13 @@ */ import type { CustomIntegrationsPluginSetup } from '@kbn/custom-integrations-plugin/server'; import { i18n } from '@kbn/i18n'; -import { ConnectorServerSideDefinition } from '@kbn/search-connectors-plugin/server'; import { ConfigType } from '.'; export const registerEnterpriseSearchIntegrations = ( config: ConfigType, - customIntegrations: CustomIntegrationsPluginSetup, - isCloud: boolean, - connectors: ConnectorServerSideDefinition[] + customIntegrations: CustomIntegrationsPluginSetup ) => { - const nativeSearchTag = config.hasNativeConnectors && isCloud ? ['native_search'] : []; if (config.hasWebCrawler) { customIntegrations.registerCustomIntegration({ id: 'web_crawler', @@ -58,29 +54,4 @@ export const registerEnterpriseSearchIntegrations = ( shipper: 'search', isBeta: false, }); - - if (config.hasConnectors) { - connectors.forEach((connector) => { - const connectorType = connector.isNative && isCloud ? 'native' : 'connector_client'; - const categories = connector.isNative - ? [...(connector.categories || []), ...nativeSearchTag] - : connector.categories; - - customIntegrations.registerCustomIntegration({ - categories: categories || [], - description: connector.description || '', - icons: [ - { - src: connector.iconPath, - type: 'svg', - }, - ], - id: `${connector.serviceType}-${connector.name}`, - isBeta: connector.isBeta, - shipper: 'search', - title: connector.name, - uiInternalPath: `/app/enterprise_search/content/connectors/new_connector?connector_type=${connectorType}&service_type=${connector.serviceType}`, - }); - }); - } }; diff --git a/x-pack/plugins/enterprise_search/server/plugin.ts b/x-pack/plugins/enterprise_search/server/plugin.ts index c80216bc7a156..4900fe2af29a2 100644 --- a/x-pack/plugins/enterprise_search/server/plugin.ts +++ b/x-pack/plugins/enterprise_search/server/plugin.ts @@ -182,12 +182,7 @@ export class EnterpriseSearchPlugin implements Plugin { const isCloud = !!cloud?.cloudId; if (customIntegrations) { - registerEnterpriseSearchIntegrations( - config, - customIntegrations, - isCloud, - searchConnectors?.getConnectorTypes() || [] - ); + registerEnterpriseSearchIntegrations(config, customIntegrations); } /* diff --git a/x-pack/plugins/fleet/common/constants/routes.ts b/x-pack/plugins/fleet/common/constants/routes.ts index 9b5c35c3b3ce2..c071c6feecbf8 100644 --- a/x-pack/plugins/fleet/common/constants/routes.ts +++ b/x-pack/plugins/fleet/common/constants/routes.ts @@ -81,6 +81,8 @@ export const AGENT_POLICY_API_ROUTES = { DELETE_PATTERN: `${AGENT_POLICY_API_ROOT}/delete`, FULL_INFO_PATTERN: `${AGENT_POLICY_API_ROOT}/{agentPolicyId}/full`, FULL_INFO_DOWNLOAD_PATTERN: `${AGENT_POLICY_API_ROOT}/{agentPolicyId}/download`, + LIST_OUTPUTS_PATTERN: `${AGENT_POLICY_API_ROOT}/outputs`, + INFO_OUTPUTS_PATTERN: `${AGENT_POLICY_API_ROOT}/{agentPolicyId}/outputs`, }; // Kubernetes Manifest API routes diff --git a/x-pack/plugins/fleet/common/services/routes.ts b/x-pack/plugins/fleet/common/services/routes.ts index ff1fb4a5ff693..520a71e1bdc0a 100644 --- a/x-pack/plugins/fleet/common/services/routes.ts +++ b/x-pack/plugins/fleet/common/services/routes.ts @@ -197,6 +197,14 @@ export const agentPolicyRouteService = { getResetAllPreconfiguredAgentPolicyPath: () => { return PRECONFIGURATION_API_ROUTES.RESET_PATTERN; }, + + getInfoOutputsPath: (agentPolicyId: string) => { + return AGENT_POLICY_API_ROUTES.INFO_OUTPUTS_PATTERN.replace('{agentPolicyId}', agentPolicyId); + }, + + getListOutputsPath: () => { + return AGENT_POLICY_API_ROUTES.LIST_OUTPUTS_PATTERN; + }, }; export const dataStreamRouteService = { diff --git a/x-pack/plugins/fleet/common/types/models/agent_policy.ts b/x-pack/plugins/fleet/common/types/models/agent_policy.ts index ebb1aa3afe7f1..ba1a0b182af72 100644 --- a/x-pack/plugins/fleet/common/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/common/types/models/agent_policy.ts @@ -262,3 +262,24 @@ export interface AgentlessApiResponse { id: string; region_id: string; } + +// Definitions for agent policy outputs endpoints +export interface MinimalOutput { + name?: string; + id?: string; +} +export interface IntegrationsOutput extends MinimalOutput { + pkgName?: string; + integrationPolicyName?: string; +} + +export interface OutputsForAgentPolicy { + agentPolicyId?: string; + monitoring: { + output: MinimalOutput; + }; + data: { + output: MinimalOutput; + integrations?: IntegrationsOutput[]; + }; +} diff --git a/x-pack/plugins/fleet/common/types/rest_spec/agent_policy.ts b/x-pack/plugins/fleet/common/types/rest_spec/agent_policy.ts index 4e6a85ccdc270..7432d1d00e61e 100644 --- a/x-pack/plugins/fleet/common/types/rest_spec/agent_policy.ts +++ b/x-pack/plugins/fleet/common/types/rest_spec/agent_policy.ts @@ -5,13 +5,19 @@ * 2.0. */ -import type { AgentPolicy, NewAgentPolicy, FullAgentPolicy } from '../models'; +import type { + AgentPolicy, + NewAgentPolicy, + FullAgentPolicy, + OutputsForAgentPolicy, +} from '../models'; import type { ListResult, ListWithKuery, BulkGetResult } from './common'; export interface GetAgentPoliciesRequest { query: ListWithKuery & { noAgentCount?: boolean; + withAgentCount?: boolean; full?: boolean; }; } @@ -92,3 +98,16 @@ export type FetchAllAgentPoliciesOptions = Pick< export type FetchAllAgentPolicyIdsOptions = Pick & { spaceId?: string; }; + +export interface GetAgentPolicyOutputsResponse { + item: OutputsForAgentPolicy; +} +export interface GetListAgentPolicyOutputsResponse { + items: OutputsForAgentPolicy[]; +} + +export interface GetListAgentPolicyOutputsRequest { + body: { + ids?: string[]; + }; +} diff --git a/x-pack/plugins/fleet/cypress/tasks/cleanup.ts b/x-pack/plugins/fleet/cypress/tasks/cleanup.ts index bad8743b66bd1..2a5ac414f49e5 100644 --- a/x-pack/plugins/fleet/cypress/tasks/cleanup.ts +++ b/x-pack/plugins/fleet/cypress/tasks/cleanup.ts @@ -8,19 +8,19 @@ import { request } from './common'; export function cleanupAgentPolicies(spaceId?: string) { - request({ url: `${spaceId ? `/s/${spaceId}` : ''}/api/fleet/agent_policies` }).then( - (response: any) => { - response.body.items - .filter((policy: any) => policy.agents === 0) - .forEach((policy: any) => { - request({ - method: 'POST', - url: `${spaceId ? `/s/${spaceId}` : ''}/api/fleet/agent_policies/delete`, - body: { agentPolicyId: policy.id }, - }); + request({ + url: `${spaceId ? `/s/${spaceId}` : ''}/api/fleet/agent_policies?withAgentCount=true`, + }).then((response: any) => { + response.body.items + .filter((policy: any) => policy.agents === 0) + .forEach((policy: any) => { + request({ + method: 'POST', + url: `${spaceId ? `/s/${spaceId}` : ''}/api/fleet/agent_policies/delete`, + body: { agentPolicyId: policy.id }, }); - } - ); + }); + }); } export function unenrollAgent() { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx index 1497b1bb0589e..6b0a7c512d197 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx @@ -53,7 +53,7 @@ import { UninstallCommandFlyout } from '../../../../../../components'; import type { ValidationResults } from '../agent_policy_validation'; import { ExperimentalFeaturesService } from '../../../../services'; - +import { useAgentPolicyFormContext } from '../agent_policy_form'; import { policyHasEndpointSecurity as hasElasticDefend } from '../../../../../../../common/services'; import { @@ -127,6 +127,8 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent = const isManagedorAgentlessPolicy = agentPolicy.is_managed === true || agentPolicy?.supports_agentless === true; + const agentPolicyFormContect = useAgentPolicyFormContext(); + const AgentTamperProtectionSectionContent = useMemo( () => ( = /> } > - - { + if (newValue.length === 0) { + return; } - onChange={(newValue) => { - if (newValue.length === 0) { - return; - } - updateAgentPolicy({ - space_ids: newValue, - }); - }} - /> - + updateAgentPolicy({ + space_ids: newValue, + }); + }} + /> ) : null} { + beforeEach(() => { + jest.mocked(useAgentPoliciesSpaces).mockReturnValue({ + data: { + items: [ + { + name: 'Default', + id: 'default', + }, + { + name: 'Test', + id: 'test', + }, + ], + }, + } as any); + }); + function render() { + const renderer = createFleetTestRendererMock(); + const onChange = jest.fn(); + const setInvalidSpaceError = jest.fn(); + const result = renderer.render( + + ); + + return { + result, + onChange, + setInvalidSpaceError, + }; + } + + it('should render invalid space errors', () => { + const { result, onChange, setInvalidSpaceError } = render(); + const inputEl = result.getByTestId('comboBoxSearchInput'); + fireEvent.change(inputEl, { + target: { value: 'invalidSpace' }, + }); + fireEvent.keyDown(inputEl, { key: 'Enter', code: 'Enter' }); + expect(result.container).toHaveTextContent('invalidSpace is not a valid space.'); + expect(onChange).not.toBeCalled(); + expect(setInvalidSpaceError).toBeCalledWith(true); + }); + + it('should clear invalid space errors', () => { + const { result, setInvalidSpaceError } = render(); + const inputEl = result.getByTestId('comboBoxSearchInput'); + fireEvent.change(inputEl, { + target: { value: 'invalidSpace' }, + }); + fireEvent.keyDown(inputEl, { key: 'Enter', code: 'Enter' }); + expect(result.container).toHaveTextContent('invalidSpace is not a valid space.'); + fireEvent.change(inputEl, { + target: { value: '' }, + }); + fireEvent.keyDown(inputEl, { key: 'Enter', code: 'Enter' }); + expect(result.container).not.toHaveTextContent('invalidSpace is not a valid space.'); + expect(setInvalidSpaceError).toBeCalledWith(false); + }); + + it('should accept valid space', () => { + const { result, onChange, setInvalidSpaceError } = render(); + const inputEl = result.getByTestId('comboBoxSearchInput'); + fireEvent.change(inputEl, { + target: { value: 'test' }, + }); + fireEvent.keyDown(inputEl, { key: 'Enter', code: 'Enter' }); + expect(result.container).not.toHaveTextContent('test is not a valid space.'); + expect(onChange).toBeCalledWith(['test']); + expect(setInvalidSpaceError).not.toBeCalledWith(true); + }); +}); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/space_selector.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/space_selector.tsx index 0532c5306d50f..53c7ed1d8226d 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/space_selector.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/space_selector.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { type EuiComboBoxOptionOption, EuiHealth } from '@elastic/eui'; +import { type EuiComboBoxOptionOption, EuiHealth, EuiFormRow } from '@elastic/eui'; import { EuiComboBox } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useMemo } from 'react'; @@ -16,11 +16,19 @@ export interface SpaceSelectorProps { value: string[]; onChange: (newVal: string[]) => void; isDisabled?: boolean; + setInvalidSpaceError?: (hasError: boolean) => void; } -export const SpaceSelector: React.FC = ({ value, onChange, isDisabled }) => { +export const SpaceSelector: React.FC = ({ + setInvalidSpaceError, + value, + onChange, + isDisabled, +}) => { const res = useAgentPoliciesSpaces(); + const [error, setError] = React.useState(); + const renderOption = React.useCallback( (option: any, searchValue: string, contentClassName: string) => ( @@ -57,20 +65,41 @@ export const SpaceSelector: React.FC = ({ value, onChange, i }, [options, value, res.isInitialLoading]); return ( - { - onChange(newOptions.map(({ key }) => key as string)); - }} - /> + key="space" + error={error} + isDisabled={isDisabled} + isInvalid={Boolean(error)} + > + { + const newError = + searchValue.length === 0 || hasMatchingOptions + ? undefined + : i18n.translate('xpack.fleet.agentPolicies.spaceSelectorInvalid', { + defaultMessage: '{space} is not a valid space.', + values: { space: searchValue }, + }); + setError(newError); + if (setInvalidSpaceError) { + setInvalidSpaceError(!!newError); + } + }} + onChange={(newOptions) => { + onChange(newOptions.map(({ key }) => key as string)); + }} + /> + ); }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_form.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_form.tsx index b437d61f64c58..8e97afcaa4d66 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_form.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_form.tsx @@ -45,12 +45,14 @@ interface Props { isEditing?: boolean; // form error state is passed up to the form updateAdvancedSettingsHasErrors: (hasErrors: boolean) => void; + setInvalidSpaceError: (hasErrors: boolean) => void; } const AgentPolicyFormContext = React.createContext< | { agentPolicy: Partial & { [key: string]: any }; updateAgentPolicy: (u: Partial) => void; updateAdvancedSettingsHasErrors: (hasErrors: boolean) => void; + setInvalidSpaceError: (hasErrors: boolean) => void; } | undefined >(undefined); @@ -67,6 +69,7 @@ export const AgentPolicyForm: React.FunctionComponent = ({ validation, isEditing = false, updateAdvancedSettingsHasErrors, + setInvalidSpaceError, }) => { const authz = useAuthz(); const isDisabled = !authz.fleet.allAgentPolicies; @@ -97,7 +100,12 @@ export const AgentPolicyForm: React.FunctionComponent = ({ return ( {!isEditing ? ( diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx index b8c46a4688e6b..ff89db3e0c842 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx @@ -154,7 +154,14 @@ afterAll(() => { consoleDebugMock.mockRestore(); }); -describe('When on the package policy create page', () => { +// FLAKY: https://github.com/elastic/kibana/issues/196463 +// FLAKY: https://github.com/elastic/kibana/issues/196464 +// FLAKY: https://github.com/elastic/kibana/issues/196465 +// FLAKY: https://github.com/elastic/kibana/issues/196466 +// FLAKY: https://github.com/elastic/kibana/issues/196467 +// FLAKY: https://github.com/elastic/kibana/issues/196468 +// FLAKY: https://github.com/elastic/kibana/issues/196469 +describe.skip('When on the package policy create page', () => { afterEach(() => { jest.clearAllMocks(); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx index 6e4f1e06b45a0..91cd710db4343 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx @@ -90,6 +90,7 @@ export const SettingsView = memo<{ agentPolicy: AgentPolicy }>( allowedNamespacePrefixes: spaceSettings?.allowedNamespacePrefixes, }); const [hasAdvancedSettingsErrors, setHasAdvancedSettingsErrors] = useState(false); + const [hasInvalidSpaceError, setInvalidSpaceError] = useState(false); const updateAgentPolicy = (updatedFields: Partial) => { setAgentPolicy({ @@ -183,6 +184,7 @@ export const SettingsView = memo<{ agentPolicy: AgentPolicy }>( validation={validation} isEditing={true} updateAdvancedSettingsHasErrors={setHasAdvancedSettingsErrors} + setInvalidSpaceError={setInvalidSpaceError} /> {hasChanges ? ( @@ -219,7 +221,8 @@ export const SettingsView = memo<{ agentPolicy: AgentPolicy }>( isDisabled={ isLoading || Object.keys(validation).length > 0 || - hasAdvancedSettingsErrors + hasAdvancedSettingsErrors || + hasInvalidSpaceError } btnProps={{ color: 'text', @@ -242,7 +245,8 @@ export const SettingsView = memo<{ agentPolicy: AgentPolicy }>( !hasAllAgentPoliciesPrivileges || isLoading || Object.keys(validation).length > 0 || - hasAdvancedSettingsErrors + hasAdvancedSettingsErrors || + hasInvalidSpaceError } data-test-subj="agentPolicyDetailsSaveButton" iconType="save" diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/components/create_agent_policy.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/components/create_agent_policy.tsx index f147f7e112ea1..a5538e7e0fa30 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/components/create_agent_policy.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/components/create_agent_policy.tsx @@ -61,6 +61,7 @@ export const CreateAgentPolicyFlyout: React.FunctionComponent = ({ allowedNamespacePrefixes: spaceSettings?.allowedNamespacePrefixes, }); const [hasAdvancedSettingsErrors, setHasAdvancedSettingsErrors] = useState(false); + const [hasInvalidSpaceError, setInvalidSpaceError] = useState(false); const updateAgentPolicy = (updatedFields: Partial) => { setAgentPolicy({ @@ -104,6 +105,7 @@ export const CreateAgentPolicyFlyout: React.FunctionComponent = ({ updateSysMonitoring={(newValue) => setWithSysMonitoring(newValue)} validation={validation} updateAdvancedSettingsHasErrors={setHasAdvancedSettingsErrors} + setInvalidSpaceError={setInvalidSpaceError} /> ); @@ -130,7 +132,10 @@ export const CreateAgentPolicyFlyout: React.FunctionComponent = ({ 0 || hasAdvancedSettingsErrors + isLoading || + Object.keys(validation).length > 0 || + hasAdvancedSettingsErrors || + hasInvalidSpaceError } description={i18n.translate( 'xpack.fleet.createAgentPolicy.devtoolsRequestDescription', @@ -150,7 +155,8 @@ export const CreateAgentPolicyFlyout: React.FunctionComponent = ({ !hasFleetAllAgentPoliciesPrivileges || isLoading || Object.keys(validation).length > 0 || - hasAdvancedSettingsErrors + hasAdvancedSettingsErrors || + hasInvalidSpaceError } onClick={async () => { setIsLoading(true); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/index.tsx index 2682a5239071d..a617629163778 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/index.tsx @@ -98,7 +98,7 @@ export const AgentPolicyListPage: React.FunctionComponent<{}> = () => { sortField: sorting?.field, sortOrder: sorting?.direction, kuery: search, - noAgentCount: false, // Explicitly fetch agent count + withAgentCount: true, // Explicitly fetch agent count full: true, }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_details/agent_details_overview.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_details/agent_details_overview.tsx index 35fd048cc13cd..2c4113c003841 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_details/agent_details_overview.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_details/agent_details_overview.tsx @@ -22,7 +22,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage, FormattedRelative } from '@kbn/i18n-react'; import type { Agent, AgentPolicy } from '../../../../../types'; -import { useAgentVersion } from '../../../../../hooks'; +import { useAgentVersion, useGetInfoOutputsForPolicy } from '../../../../../hooks'; import { ExperimentalFeaturesService, isAgentUpgradeable } from '../../../../../services'; import { AgentPolicySummaryLine } from '../../../../../components'; import { AgentHealth } from '../../../components'; @@ -30,6 +30,7 @@ import { Tags } from '../../../components/tags'; import { formatAgentCPU, formatAgentMemory } from '../../../services/agent_metrics'; import { AgentDashboardLink } from '../agent_dashboard_link'; import { AgentUpgradeStatus } from '../../../agent_list_page/components/agent_upgrade_status'; +import { AgentPolicyOutputsSummary } from '../../../agent_list_page/components/agent_policy_outputs_summary'; // Allows child text to be truncated const FlexItemWithMinWidth = styled(EuiFlexItem)` @@ -43,10 +44,17 @@ export const AgentDetailsOverviewSection: React.FunctionComponent<{ const latestAgentVersion = useAgentVersion(); const { displayAgentMetrics } = ExperimentalFeaturesService.get(); + const outputRes = useGetInfoOutputsForPolicy(agentPolicy?.id); + const outputs = outputRes?.data?.item; + return ( - + {displayAgentMetrics && ( @@ -206,6 +214,22 @@ export const AgentDetailsOverviewSection: React.FunctionComponent<{ ? agent.local_metadata.host.id : '-', }, + { + title: i18n.translate('xpack.fleet.agentDetails.outputForMonitoringLabel', { + defaultMessage: 'Output for integrations', + }), + description: outputs ? : '-', + }, + { + title: i18n.translate('xpack.fleet.agentDetails.outputForMonitoringLabel', { + defaultMessage: 'Output for monitoring', + }), + description: outputs ? ( + + ) : ( + '-' + ), + }, { title: i18n.translate('xpack.fleet.agentDetails.logLevel', { defaultMessage: 'Logging level', diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_list_table.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_list_table.tsx index 4c6c83dd7145e..d70ed67247207 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_list_table.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_list_table.tsx @@ -4,7 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React from 'react'; +import React, { useCallback, useMemo } from 'react'; +import type { EuiBasicTableColumn } from '@elastic/eui'; import { type CriteriaWithPagination } from '@elastic/eui'; import { EuiBasicTable, @@ -23,20 +24,31 @@ import { isAgentUpgradeable, ExperimentalFeaturesService } from '../../../../ser import { AgentHealth } from '../../components'; import type { Pagination } from '../../../../hooks'; -import { useAgentVersion } from '../../../../hooks'; +import { useAgentVersion, useGetListOutputsForPolicies } from '../../../../hooks'; import { useLink, useAuthz } from '../../../../hooks'; import { AgentPolicySummaryLine } from '../../../../components'; import { Tags } from '../../components/tags'; -import type { AgentMetrics } from '../../../../../../../common/types'; +import type { AgentMetrics, OutputsForAgentPolicy } from '../../../../../../../common/types'; import { formatAgentCPU, formatAgentMemory } from '../../services/agent_metrics'; +import { AgentPolicyOutputsSummary } from './agent_policy_outputs_summary'; + import { AgentUpgradeStatus } from './agent_upgrade_status'; import { EmptyPrompt } from './empty_prompt'; -const VERSION_FIELD = 'local_metadata.elastic.agent.version'; -const HOSTNAME_FIELD = 'local_metadata.host.hostname'; +const AGENTS_TABLE_FIELDS = { + ACTIVE: 'active', + HOSTNAME: 'local_metadata.host.hostname', + POLICY: 'policy_id', + METRICS: 'metrics', + VERSION: 'local_metadata.elastic.agent.version', + LAST_CHECKIN: 'last_checkin', + OUTPUT_INTEGRATION: 'output_integrations', + OUTPUT_MONITORING: 'output_monitoring', +}; + function safeMetadata(val: any) { if (typeof val !== 'string') { return '-'; @@ -96,14 +108,33 @@ export const AgentListTable: React.FC = (props: Props) => { const { getHref } = useLink(); const latestAgentVersion = useAgentVersion(); - const isAgentSelectable = (agent: Agent) => { - if (!agent.active) return false; - if (!agent.policy_id) return true; + const isAgentSelectable = useCallback( + (agent: Agent) => { + if (!agent.active) return false; + if (!agent.policy_id) return true; - const agentPolicy = agentPoliciesIndexedById[agent.policy_id]; - const isHosted = agentPolicy?.is_managed === true; - return !isHosted; - }; + const agentPolicy = agentPoliciesIndexedById[agent.policy_id]; + const isHosted = agentPolicy?.is_managed === true; + return !isHosted; + }, + [agentPoliciesIndexedById] + ); + + const agentsShown = useMemo(() => { + return totalAgents + ? showUpgradeable + ? agents.filter((agent) => isAgentSelectable(agent) && isAgentUpgradeable(agent)) + : agents + : []; + }, [agents, isAgentSelectable, showUpgradeable, totalAgents]); + + // get the policyIds of the agents shown on the page + const policyIds = useMemo(() => { + return agentsShown.map((agent) => agent?.policy_id ?? ''); + }, [agentsShown]); + const allOutputs = useGetListOutputsForPolicies({ + ids: policyIds, + }); const noItemsMessage = isLoading && isCurrentRequestIncremented ? ( @@ -140,9 +171,9 @@ export const AgentListTable: React.FC = (props: Props) => { }, }; - const columns = [ + const columns: Array> = [ { - field: 'active', + field: AGENTS_TABLE_FIELDS.ACTIVE, sortable: false, width: '85px', name: i18n.translate('xpack.fleet.agentList.statusColumnTitle', { @@ -151,7 +182,7 @@ export const AgentListTable: React.FC = (props: Props) => { render: (active: boolean, agent: any) => , }, { - field: HOSTNAME_FIELD, + field: AGENTS_TABLE_FIELDS.HOSTNAME, sortable: true, name: i18n.translate('xpack.fleet.agentList.hostColumnTitle', { defaultMessage: 'Host', @@ -171,7 +202,7 @@ export const AgentListTable: React.FC = (props: Props) => { ), }, { - field: 'policy_id', + field: AGENTS_TABLE_FIELDS.POLICY, sortable: true, truncateText: true, name: i18n.translate('xpack.fleet.agentList.policyColumnTitle', { @@ -208,7 +239,7 @@ export const AgentListTable: React.FC = (props: Props) => { ...(displayAgentMetrics ? [ { - field: 'metrics', + field: AGENTS_TABLE_FIELDS.METRICS, sortable: false, name: ( = (props: Props) => { ), }, { - field: 'metrics', + field: AGENTS_TABLE_FIELDS.METRICS, sortable: false, name: ( = (props: Props) => { }, ] : []), - { - field: 'last_checkin', + field: AGENTS_TABLE_FIELDS.LAST_CHECKIN, sortable: true, name: i18n.translate('xpack.fleet.agentList.lastCheckinTitle', { defaultMessage: 'Last activity', }), + width: '100px', + render: (lastCheckin: string) => + lastCheckin ? : undefined, + }, + { + field: AGENTS_TABLE_FIELDS.OUTPUT_INTEGRATION, + sortable: true, + truncateText: true, + name: i18n.translate('xpack.fleet.agentList.integrationsOutputTitle', { + defaultMessage: 'Output for integrations', + }), + width: '180px', + render: (outputs: OutputsForAgentPolicy[], agent: Agent) => { + if (!agent?.policy_id) return null; + + const outputsForPolicy = allOutputs?.data?.items.find( + (item) => item.agentPolicyId === agent?.policy_id + ); + return ; + }, + }, + { + field: AGENTS_TABLE_FIELDS.OUTPUT_MONITORING, + sortable: true, + truncateText: true, + name: i18n.translate('xpack.fleet.agentList.monitoringOutputTitle', { + defaultMessage: 'Output for monitoring', + }), width: '180px', - render: (lastCheckin: string, agent: any) => - lastCheckin ? : null, + render: (outputs: OutputsForAgentPolicy[], agent: Agent) => { + if (!agent?.policy_id) return null; + + const outputsForPolicy = allOutputs?.data?.items.find( + (item) => item.agentPolicyId === agent?.policy_id + ); + return ; + }, }, { - field: VERSION_FIELD, + field: AGENTS_TABLE_FIELDS.VERSION, sortable: true, width: '220px', name: i18n.translate('xpack.fleet.agentList.versionTitle', { @@ -322,13 +386,7 @@ export const AgentListTable: React.FC = (props: Props) => { data-test-subj="fleetAgentListTable" loading={isLoading} noItemsMessage={noItemsMessage} - items={ - totalAgents - ? showUpgradeable - ? agents.filter((agent) => isAgentSelectable(agent) && isAgentUpgradeable(agent)) - : agents - : [] - } + items={agentsShown} itemId="id" columns={columns} pagination={{ diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_policy_outputs_summary.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_policy_outputs_summary.test.tsx new file mode 100644 index 0000000000000..255b2efb94026 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_policy_outputs_summary.test.tsx @@ -0,0 +1,99 @@ +/* + * Copyright 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, fireEvent } from '@testing-library/react'; +import React from 'react'; + +import { createFleetTestRendererMock } from '../../../../../../mock'; +import type { TestRenderer } from '../../../../../../mock'; + +import type { OutputsForAgentPolicy } from '../../../../../../../common/types'; + +import { AgentPolicyOutputsSummary } from './agent_policy_outputs_summary'; + +describe('MultipleAgentPolicySummaryLine', () => { + let testRenderer: TestRenderer; + const outputsForPolicy: OutputsForAgentPolicy = { + agentPolicyId: 'policy-1', + monitoring: { + output: { + id: 'elasticsearch1', + name: 'Elasticsearch1', + }, + }, + data: { + output: { + id: 'elasticsearch1', + name: 'Elasticsearch1', + }, + }, + }; + const data = { + data: { + output: { + id: 'elasticsearch1', + name: 'Elasticsearch1', + }, + integrations: [ + { + id: 'remote_es1', + name: 'Remote ES', + pkgName: 'ngnix', + integrationPolicyName: 'Nginx-1', + }, + + { + id: 'logstash', + name: 'Logstash-1', + pkgName: 'apache', + integrationPolicyName: 'Apache-1', + }, + ], + }, + }; + + const render = (outputs?: OutputsForAgentPolicy, isMonitoring?: boolean) => + testRenderer.render( + + ); + + beforeEach(() => { + testRenderer = createFleetTestRendererMock(); + }); + + test('it should render the name associated with the default output when the agent policy does not have custom outputs', async () => { + const results = render(outputsForPolicy); + expect(results.container.textContent).toBe('Elasticsearch1'); + expect(results.queryByTestId('outputNameLink')).toBeInTheDocument(); + expect(results.queryByTestId('outputsIntegrationsNumberBadge')).not.toBeInTheDocument(); + }); + + test('it should render the first output name and the badge when there are multiple outputs associated with integrations', async () => { + const results = render({ ...outputsForPolicy, ...data }); + + expect(results.queryByTestId('outputNameLink')).toBeInTheDocument(); + expect(results.queryByTestId('outputsIntegrationsNumberBadge')).toBeInTheDocument(); + + await act(async () => { + fireEvent.click(results.getByTestId('outputsIntegrationsNumberBadge')); + }); + expect(results.queryByTestId('outputPopover')).toBeInTheDocument(); + expect(results.queryByTestId('output-integration-0')?.textContent).toContain( + 'Nginx-1: Remote ES' + ); + expect(results.queryByTestId('output-integration-1')?.textContent).toContain( + 'Apache-1: Logstash-1' + ); + }); + + test('it should not render the badge when monitoring is true', async () => { + const results = render({ ...outputsForPolicy, ...data }, true); + + expect(results.queryByTestId('outputNameLink')).toBeInTheDocument(); + expect(results.queryByTestId('outputsIntegrationsNumberBadge')).not.toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_policy_outputs_summary.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_policy_outputs_summary.tsx new file mode 100644 index 0000000000000..c0b0e5fbfbccc --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_policy_outputs_summary.tsx @@ -0,0 +1,115 @@ +/* + * Copyright 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 styled from 'styled-components'; +import React, { useMemo, useState } from 'react'; + +import type { EuiListGroupItemProps } from '@elastic/eui'; +import { + EuiBadge, + EuiFlexGroup, + EuiFlexItem, + EuiLink, + EuiListGroup, + EuiPopover, + EuiPopoverTitle, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { useLink } from '../../../../hooks'; +import type { OutputsForAgentPolicy } from '../../../../../../../common/types'; + +const TruncatedEuiLink = styled(EuiLink)` + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + width: 120px; +`; + +export const AgentPolicyOutputsSummary: React.FC<{ + outputs?: OutputsForAgentPolicy; + isMonitoring?: boolean; +}> = ({ outputs, isMonitoring }) => { + const { getHref } = useLink(); + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const closePopover = () => setIsPopoverOpen(false); + + const monitoring = outputs?.monitoring; + const data = outputs?.data; + + const listItems: EuiListGroupItemProps[] = useMemo(() => { + if (!data?.integrations) return []; + + return (data?.integrations || []).map((integration, index) => { + return { + 'data-test-subj': `output-integration-${index}`, + label: `${integration.integrationPolicyName}: ${integration.name}`, + href: getHref('settings_edit_outputs', { outputId: integration?.id ?? '' }), + iconType: 'dot', + }; + }); + }, [getHref, data?.integrations]); + + return ( + + {isMonitoring ? ( + + + {monitoring?.output.name} + + + ) : ( + + + {data?.output.name} + + + )} + + {data?.integrations && data?.integrations.length >= 1 && !isMonitoring && ( + + setIsPopoverOpen(!isPopoverOpen)} + onClickAriaLabel="Open output integrations popover" + > + +{data?.integrations.length} + + + + {i18n.translate('xpack.fleet.AgentPolicyOutputsSummary.popover.title', { + defaultMessage: 'Output for integrations', + })} + +
+ +
+
+
+ )} +
+ ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/agent_policy_debugger.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/agent_policy_debugger.tsx index 98eed3cb63b4d..58e9a4ac074f7 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/agent_policy_debugger.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/agent_policy_debugger.tsx @@ -22,7 +22,6 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { sendGetAgentPolicies, useLink } from '../../../hooks'; -import { SO_SEARCH_LIMIT } from '../../../constants'; import { policyHasFleetServer } from '../../../services'; import type { AgentPolicy } from '../../../types'; @@ -35,7 +34,7 @@ import { CodeBlock } from './code_block'; const fetchAgentPolicies = async () => { const response = await sendGetAgentPolicies({ full: true, - perPage: SO_SEARCH_LIMIT, + perPage: 100, sortOrder: 'asc', }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/preconfiguration_debugger.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/preconfiguration_debugger.tsx index f140e4068d040..3072c87636479 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/preconfiguration_debugger.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/preconfiguration_debugger.tsx @@ -29,7 +29,7 @@ import { useLink, useStartServices, } from '../../../hooks'; -import { LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE, SO_SEARCH_LIMIT } from '../../../constants'; +import { LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE } from '../../../constants'; import { queryClient } from '..'; import { CodeBlock } from './code_block'; @@ -37,7 +37,7 @@ import { CodeBlock } from './code_block'; const fetchPreconfiguredPolicies = async () => { const kuery = `${LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE}.is_preconfigured:true`; - const response = await sendGetAgentPolicies({ kuery, perPage: SO_SEARCH_LIMIT, full: true }); + const response = await sendGetAgentPolicies({ kuery, perPage: 100, full: true }); if (response.error) { throw new Error(response.error.message); diff --git a/x-pack/plugins/fleet/public/hooks/use_request/agent_policy.ts b/x-pack/plugins/fleet/public/hooks/use_request/agent_policy.ts index 9e4fb2344fc29..e130eae49c6eb 100644 --- a/x-pack/plugins/fleet/public/hooks/use_request/agent_policy.ts +++ b/x-pack/plugins/fleet/public/hooks/use_request/agent_policy.ts @@ -23,6 +23,9 @@ import type { DeleteAgentPolicyRequest, DeleteAgentPolicyResponse, BulkGetAgentPoliciesResponse, + GetAgentPolicyOutputsResponse, + GetListAgentPolicyOutputsResponse, + GetListAgentPolicyOutputsRequest, } from '../../types'; import { useRequest, sendRequest, useConditionalRequest, sendRequestForRq } from './use_request'; @@ -201,3 +204,21 @@ export const sendResetAllPreconfiguredAgentPolicies = () => { version: API_VERSIONS.internal.v1, }); }; + +export const useGetListOutputsForPolicies = (body?: GetListAgentPolicyOutputsRequest['body']) => { + return useRequest({ + path: agentPolicyRouteService.getListOutputsPath(), + method: 'post', + body: JSON.stringify(body), + version: API_VERSIONS.public.v1, + }); +}; + +export const useGetInfoOutputsForPolicy = (agentPolicyId: string | undefined) => { + return useConditionalRequest({ + path: agentPolicyId ? agentPolicyRouteService.getInfoOutputsPath(agentPolicyId) : undefined, + method: 'get', + shouldSendRequest: !!agentPolicyId, + version: API_VERSIONS.public.v1, + } as SendConditionalRequestConfig); +}; diff --git a/x-pack/plugins/fleet/public/types/index.ts b/x-pack/plugins/fleet/public/types/index.ts index 099df2ce5a34f..0f0adaba20b5d 100644 --- a/x-pack/plugins/fleet/public/types/index.ts +++ b/x-pack/plugins/fleet/public/types/index.ts @@ -147,6 +147,9 @@ export type { GetEnrollmentSettingsRequest, GetEnrollmentSettingsResponse, GetSpaceSettingsResponse, + GetAgentPolicyOutputsResponse, + GetListAgentPolicyOutputsRequest, + GetListAgentPolicyOutputsResponse, } from '../../common/types'; export { entries, diff --git a/x-pack/plugins/fleet/server/routes/agent/index.ts b/x-pack/plugins/fleet/server/routes/agent/index.ts index f3b6e28a90f37..fc45869dc1219 100644 --- a/x-pack/plugins/fleet/server/routes/agent/index.ts +++ b/x-pack/plugins/fleet/server/routes/agent/index.ts @@ -397,6 +397,7 @@ export const registerAPIRoutes = (router: FleetAuthzRouter, config: FleetConfigT fleetAuthz: { fleet: { allAgents: true }, }, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }) .addVersion( @@ -618,6 +619,7 @@ export const registerAPIRoutes = (router: FleetAuthzRouter, config: FleetConfigT fleetAuthz: { fleet: { readAgents: true }, }, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }) .addVersion( diff --git a/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts b/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts index d22d8790ef481..49b5590a2e761 100644 --- a/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts @@ -33,6 +33,8 @@ import type { BulkGetAgentPoliciesRequestSchema, AgentPolicy, FleetRequestHandlerContext, + GetAgentPolicyOutputsRequestSchema, + GetListAgentPolicyOutputsRequestSchema, } from '../../types'; import type { @@ -47,6 +49,8 @@ import type { GetFullAgentConfigMapResponse, GetFullAgentManifestResponse, BulkGetAgentPoliciesResponse, + GetAgentPolicyOutputsResponse, + GetListAgentPolicyOutputsResponse, } from '../../../common/types'; import { defaultFleetErrorHandler, @@ -133,7 +137,8 @@ export const getAgentPoliciesHandler: FleetRequestHandler< const esClient = coreContext.elasticsearch.client.asInternalUser; const { full: withPackagePolicies = false, - noAgentCount = false, + noAgentCount, + withAgentCount, format, ...restOfQuery } = request.query; @@ -150,7 +155,7 @@ export const getAgentPoliciesHandler: FleetRequestHandler< let { items } = agentPoliciesResponse; const { total, page, perPage } = agentPoliciesResponse; - if (fleetContext.authz.fleet.readAgents && !noAgentCount) { + if (fleetContext.authz.fleet.readAgents && (noAgentCount === false || withAgentCount)) { await populateAssignedAgentsCount(fleetContext.agentClient.asCurrentUser, items); } @@ -677,3 +682,64 @@ export const downloadK8sManifest: FleetRequestHandler< return defaultFleetErrorHandler({ error, response }); } }; + +export const GetAgentPolicyOutputsHandler: FleetRequestHandler< + TypeOf, + undefined +> = async (context, request, response) => { + try { + const coreContext = await context.core; + const soClient = coreContext.savedObjects.client; + const agentPolicy = await agentPolicyService.get(soClient, request.params.agentPolicyId); + + if (!agentPolicy) { + return response.customError({ + statusCode: 404, + body: { message: 'Agent policy not found' }, + }); + } + const outputs = await agentPolicyService.getAllOutputsForPolicy(soClient, agentPolicy); + + const body: GetAgentPolicyOutputsResponse = { + item: outputs, + }; + return response.ok({ + body, + }); + } catch (error) { + return defaultFleetErrorHandler({ error, response }); + } +}; + +export const GetListAgentPolicyOutputsHandler: FleetRequestHandler< + undefined, + undefined, + TypeOf +> = async (context, request, response) => { + try { + const coreContext = await context.core; + const soClient = coreContext.savedObjects.client; + const { ids } = request.body; + + if (!ids) { + return response.ok({ + body: { items: [] }, + }); + } + const agentPolicies = await agentPolicyService.getByIDs(soClient, ids, { + withPackagePolicies: true, + }); + + const outputsList = await agentPolicyService.listAllOutputsForPolicies(soClient, agentPolicies); + + const body: GetListAgentPolicyOutputsResponse = { + items: outputsList, + }; + + return response.ok({ + body, + }); + } catch (error) { + return defaultFleetErrorHandler({ error, response }); + } +}; diff --git a/x-pack/plugins/fleet/server/routes/agent_policy/index.ts b/x-pack/plugins/fleet/server/routes/agent_policy/index.ts index 2ed7079deceec..9311f0ae2acca 100644 --- a/x-pack/plugins/fleet/server/routes/agent_policy/index.ts +++ b/x-pack/plugins/fleet/server/routes/agent_policy/index.ts @@ -28,6 +28,10 @@ import { GetFullAgentPolicyResponseSchema, DownloadFullAgentPolicyResponseSchema, GetK8sManifestResponseScheme, + GetAgentPolicyOutputsRequestSchema, + GetAgentPolicyOutputsResponseSchema, + GetListAgentPolicyOutputsResponseSchema, + GetListAgentPolicyOutputsRequestSchema, } from '../../types'; import { K8S_API_ROUTES } from '../../../common/constants'; @@ -47,6 +51,8 @@ import { downloadK8sManifest, getK8sManifest, bulkGetAgentPoliciesHandler, + GetAgentPolicyOutputsHandler, + GetListAgentPolicyOutputsHandler, } from './handlers'; export const registerRoutes = (router: FleetAuthzRouter) => { @@ -390,4 +396,62 @@ export const registerRoutes = (router: FleetAuthzRouter) => { }, downloadK8sManifest ); + + router.versioned + .post({ + path: AGENT_POLICY_API_ROUTES.LIST_OUTPUTS_PATTERN, + fleetAuthz: (authz) => { + return authz.fleet.readAgentPolicies && authz.fleet.readSettings; + }, + description: `Get list of outputs associated with agent policies`, + options: { + tags: ['oas-tag:Elastic Agent policies'], + }, + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: { + request: GetListAgentPolicyOutputsRequestSchema, + response: { + 200: { + body: () => GetListAgentPolicyOutputsResponseSchema, + }, + 400: { + body: genericErrorResponse, + }, + }, + }, + }, + GetListAgentPolicyOutputsHandler + ); + + router.versioned + .get({ + path: AGENT_POLICY_API_ROUTES.INFO_OUTPUTS_PATTERN, + fleetAuthz: (authz) => { + return authz.fleet.readAgentPolicies && authz.fleet.readSettings; + }, + description: `Get list of outputs associated with agent policy by policy id`, + options: { + tags: ['oas-tag:Elastic Agent policies'], + }, + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: { + request: GetAgentPolicyOutputsRequestSchema, + response: { + 200: { + body: () => GetAgentPolicyOutputsResponseSchema, + }, + 400: { + body: genericErrorResponse, + }, + }, + }, + }, + GetAgentPolicyOutputsHandler + ); }; diff --git a/x-pack/plugins/fleet/server/routes/app/index.ts b/x-pack/plugins/fleet/server/routes/app/index.ts index ea5d7be8156d5..c0b7dbcfa1743 100644 --- a/x-pack/plugins/fleet/server/routes/app/index.ts +++ b/x-pack/plugins/fleet/server/routes/app/index.ts @@ -293,6 +293,7 @@ export const registerRoutes = (router: FleetAuthzRouter, config: FleetConfigType fleet: { allAgents: true }, }, description: `Create a service token`, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }) .addVersion( diff --git a/x-pack/plugins/fleet/server/routes/enrollment_api_key/index.ts b/x-pack/plugins/fleet/server/routes/enrollment_api_key/index.ts index a93f63822e5b4..bcf4448420919 100644 --- a/x-pack/plugins/fleet/server/routes/enrollment_api_key/index.ts +++ b/x-pack/plugins/fleet/server/routes/enrollment_api_key/index.ts @@ -161,6 +161,7 @@ export const registerRoutes = (router: FleetAuthzRouter) => { fleetAuthz: { fleet: { readEnrollmentTokens: true }, }, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }) .addVersion( @@ -177,6 +178,7 @@ export const registerRoutes = (router: FleetAuthzRouter) => { fleetAuthz: { fleet: { allAgents: true }, }, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }) .addVersion( @@ -193,6 +195,7 @@ export const registerRoutes = (router: FleetAuthzRouter) => { fleetAuthz: { fleet: { readEnrollmentTokens: true }, }, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }) .addVersion( @@ -209,6 +212,7 @@ export const registerRoutes = (router: FleetAuthzRouter) => { fleetAuthz: { fleet: { allAgents: true }, }, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }) .addVersion( diff --git a/x-pack/plugins/fleet/server/routes/epm/index.ts b/x-pack/plugins/fleet/server/routes/epm/index.ts index c7eddee19b9e0..0e3c5e76eb825 100644 --- a/x-pack/plugins/fleet/server/routes/epm/index.ts +++ b/x-pack/plugins/fleet/server/routes/epm/index.ts @@ -659,6 +659,7 @@ export const registerRoutes = (router: FleetAuthzRouter, config: FleetConfigType fleetAuthz, getRouteRequiredAuthz('get', EPM_API_ROUTES.INFO_PATTERN_DEPRECATED) ).granted, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }) .addVersion( @@ -687,6 +688,7 @@ export const registerRoutes = (router: FleetAuthzRouter, config: FleetConfigType fleetAuthz: { integrations: { writePackageSettings: true }, }, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }) .addVersion( @@ -713,6 +715,7 @@ export const registerRoutes = (router: FleetAuthzRouter, config: FleetConfigType .post({ path: EPM_API_ROUTES.INSTALL_FROM_REGISTRY_PATTERN_DEPRECATED, fleetAuthz: INSTALL_PACKAGES_AUTHZ, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }) .addVersion( @@ -741,6 +744,7 @@ export const registerRoutes = (router: FleetAuthzRouter, config: FleetConfigType fleetAuthz: { integrations: { removePackages: true }, }, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }) .addVersion( diff --git a/x-pack/plugins/fleet/server/routes/health_check/handler.ts b/x-pack/plugins/fleet/server/routes/health_check/handler.ts index d6a782d271d9d..f191caa3fff1b 100644 --- a/x-pack/plugins/fleet/server/routes/health_check/handler.ts +++ b/x-pack/plugins/fleet/server/routes/health_check/handler.ts @@ -5,8 +5,6 @@ * 2.0. */ -import https from 'https'; - import type { TypeOf } from '@kbn/config-schema'; import fetch from 'node-fetch'; @@ -60,9 +58,6 @@ export const postHealthCheckHandler: FleetRequestHandler< accept: '*/*', }, method: 'GET', - agent: new https.Agent({ - rejectUnauthorized: false, - }), signal: abortController.signal, }); const bodyRes = await res.json(); diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts index fa5522d50802b..609c560906de2 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts @@ -109,7 +109,7 @@ jest.mock('../output', () => { getDefaultDataOutputId: async () => 'test-id', getDefaultMonitoringOutputId: async () => 'test-id', get: (soClient: any, id: string): Output => OUTPUTS[id] || OUTPUTS['test-id'], - bulkGet: async (soClient: any, ids: string[]): Promise => { + bulkGet: async (ids: string[]): Promise => { return ids.map((id) => OUTPUTS[id] || OUTPUTS['test-id']); }, }, diff --git a/x-pack/plugins/fleet/server/services/agent_policies/related_saved_objects.ts b/x-pack/plugins/fleet/server/services/agent_policies/related_saved_objects.ts index 52dd34a757693..c10edcfeb6edf 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/related_saved_objects.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/related_saved_objects.ts @@ -48,7 +48,7 @@ export async function fetchRelatedSavedObjects( const [outputs, { host: downloadSourceUri, proxy_id: downloadSourceProxyId }, fleetServerHosts] = await Promise.all([ - outputService.bulkGet(soClient, outputIds, { ignoreNotFound: true }), + outputService.bulkGet(outputIds, { ignoreNotFound: true }), getSourceUriForAgentPolicy(soClient, agentPolicy), getFleetServerHostsForAgentPolicy(soClient, agentPolicy).catch((err) => { appContextService diff --git a/x-pack/plugins/fleet/server/services/agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policy.ts index 8ce8c9a4291bc..0209ee6edb630 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { chunk, groupBy, isEqual, keyBy, omit, pick } from 'lodash'; +import { chunk, groupBy, isEqual, keyBy, omit, pick, uniq } from 'lodash'; import { v5 as uuidv5 } from 'uuid'; import { dump } from 'js-yaml'; import pMap from 'p-map'; @@ -61,6 +61,7 @@ import type { PostAgentPolicyCreateCallback, PostAgentPolicyUpdateCallback, PreconfiguredAgentPolicy, + OutputsForAgentPolicy, } from '../types'; import { AGENT_POLICY_INDEX, @@ -74,6 +75,7 @@ import type { FetchAllAgentPoliciesOptions, FetchAllAgentPolicyIdsOptions, FleetServerPolicy, + IntegrationsOutput, PackageInfo, } from '../../common/types'; import { @@ -85,6 +87,7 @@ import { HostedAgentPolicyRestrictionRelatedError, PackagePolicyRestrictionRelatedError, AgentlessPolicyExistsRequestError, + OutputNotFoundError, } from '../errors'; import type { FullAgentConfigMap } from '../../common/types/models/agent_cm'; @@ -1777,6 +1780,95 @@ class AgentPolicyService { }); } + // Get all the outputs per agent policy + public async getAllOutputsForPolicy( + soClient: SavedObjectsClientContract, + agentPolicy: AgentPolicy + ) { + const logger = appContextService.getLogger(); + + const [defaultDataOutputId, defaultMonitoringOutputId] = await Promise.all([ + outputService.getDefaultDataOutputId(soClient), + outputService.getDefaultMonitoringOutputId(soClient), + ]); + + if (!defaultDataOutputId) { + throw new OutputNotFoundError('Default output is not setup'); + } + + const dataOutputId = agentPolicy.data_output_id || defaultDataOutputId; + const monitoringOutputId = + agentPolicy.monitoring_output_id || defaultMonitoringOutputId || dataOutputId; + + const outputIds = uniq([dataOutputId, monitoringOutputId]); + + const fetchedOutputs = await outputService.bulkGet(outputIds, { + ignoreNotFound: true, + }); + + const dataOutput = fetchedOutputs.find((output) => output.id === dataOutputId); + const monitoringOutput = fetchedOutputs.find((output) => output.id === monitoringOutputId); + + let integrationsDataOutputs: IntegrationsOutput[] = []; + if (agentPolicy?.package_policies) { + const integrationsWithOutputs = agentPolicy.package_policies.filter( + (pkgPolicy) => !!pkgPolicy?.output_id + ); + integrationsDataOutputs = await pMap( + integrationsWithOutputs, + async (pkgPolicy) => { + if (pkgPolicy?.output_id) { + try { + const output = await outputService.get(soClient, pkgPolicy.output_id); + return { integrationPolicyName: pkgPolicy?.name, id: output.id, name: output.name }; + } catch (error) { + logger.error( + `error while retrieving output with id "${pkgPolicy.output_id}": ${error}` + ); + } + } + return { integrationPolicyName: pkgPolicy?.name, id: pkgPolicy?.output_id ?? '' }; + }, + { + concurrency: 20, + } + ); + } + const outputs: OutputsForAgentPolicy = { + monitoring: { + output: { + name: monitoringOutput?.name ?? '', + id: monitoringOutput?.id ?? '', + }, + }, + data: { + output: { + name: dataOutput?.name ?? '', + id: dataOutput?.id ?? '', + }, + integrations: integrationsDataOutputs ?? [], + }, + }; + return outputs; + } + + public async listAllOutputsForPolicies( + soClient: SavedObjectsClientContract, + agentPolicies: AgentPolicy[] + ) { + const allOutputs: OutputsForAgentPolicy[] = await pMap( + agentPolicies, + async (agentPolicy) => { + const output = await this.getAllOutputsForPolicy(soClient, agentPolicy); + return { agentPolicyId: agentPolicy.id, ...output }; + }, + { + concurrency: 50, + } + ); + return allOutputs; + } + private checkTamperProtectionLicense(agentPolicy: { is_protected?: boolean }): void { if (agentPolicy?.is_protected && !licenseService.isPlatinum()) { throw new FleetUnauthorizedError('Tamper protection requires Platinum license'); diff --git a/x-pack/plugins/fleet/server/services/output.ts b/x-pack/plugins/fleet/server/services/output.ts index 283e28417998b..b5041d9f1df37 100644 --- a/x-pack/plugins/fleet/server/services/output.ts +++ b/x-pack/plugins/fleet/server/services/output.ts @@ -705,11 +705,7 @@ class OutputService { return outputSavedObjectToOutput(newSo); } - public async bulkGet( - soClient: SavedObjectsClientContract, - ids: string[], - { ignoreNotFound = false } = { ignoreNotFound: true } - ) { + public async bulkGet(ids: string[], { ignoreNotFound = false } = { ignoreNotFound: true }) { const res = await this.encryptedSoClient.bulkGet( ids.map((id) => ({ id: outputIdToUuid(id), type: SAVED_OBJECT_TYPE })) ); diff --git a/x-pack/plugins/fleet/server/services/preconfiguration/outputs.ts b/x-pack/plugins/fleet/server/services/preconfiguration/outputs.ts index 452a73282144e..68b4cf2457e26 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration/outputs.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration/outputs.ts @@ -74,7 +74,6 @@ export async function createOrUpdatePreconfiguredOutputs( } const existingOutputs = await outputService.bulkGet( - soClient, outputs.map(({ id }) => id), { ignoreNotFound: true } ); diff --git a/x-pack/plugins/fleet/server/types/index.tsx b/x-pack/plugins/fleet/server/types/index.tsx index 786db010c8eed..5d118af97f478 100644 --- a/x-pack/plugins/fleet/server/types/index.tsx +++ b/x-pack/plugins/fleet/server/types/index.tsx @@ -101,6 +101,7 @@ export type { InstallLatestExecutedState, TemplateAgentPolicyInput, NewPackagePolicyInput, + OutputsForAgentPolicy, } from '../../common/types'; export { ElasticsearchAssetType, KibanaAssetType, KibanaSavedObjectType } from '../../common/types'; export { dataTypes } from '../../common/constants'; diff --git a/x-pack/plugins/fleet/server/types/models/agent_policy.ts b/x-pack/plugins/fleet/server/types/models/agent_policy.ts index 09c32aeb91af6..e82063e775d70 100644 --- a/x-pack/plugins/fleet/server/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/server/types/models/agent_policy.ts @@ -393,3 +393,35 @@ export const FullAgentPolicyResponseSchema = schema.object({ }) ), }); +const MinimalOutputSchema = schema.object({ + id: schema.string(), + name: schema.string(), +}); + +const IntegrationsOutputSchema = schema.arrayOf( + schema.object({ + pkgName: schema.maybe(schema.string()), + integrationPolicyName: schema.maybe(schema.string()), + id: schema.maybe(schema.string()), + name: schema.maybe(schema.string()), + }) +); + +const OutputsForAgentPolicySchema = schema.object({ + agentPolicyId: schema.maybe(schema.string()), + monitoring: schema.object({ + output: MinimalOutputSchema, + }), + data: schema.object({ + output: MinimalOutputSchema, + integrations: schema.maybe(IntegrationsOutputSchema), + }), +}); + +export const GetAgentPolicyOutputsResponseSchema = schema.object({ + item: OutputsForAgentPolicySchema, +}); + +export const GetListAgentPolicyOutputsResponseSchema = schema.object({ + items: schema.arrayOf(OutputsForAgentPolicySchema), +}); diff --git a/x-pack/plugins/fleet/server/types/rest_spec/agent_policy.test.ts b/x-pack/plugins/fleet/server/types/rest_spec/agent_policy.test.ts new file mode 100644 index 0000000000000..da9e7f962b158 --- /dev/null +++ b/x-pack/plugins/fleet/server/types/rest_spec/agent_policy.test.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 { GetAgentPoliciesRequestSchema } from './agent_policy'; + +describe('GetAgentPoliciesRequestSchema.query', () => { + it('should work without query parameters', () => { + expect(() => GetAgentPoliciesRequestSchema.query.validate({})).not.toThrow(); + }); + + it('should work with perPage being less than 100', () => { + expect(() => GetAgentPoliciesRequestSchema.query.validate({ perPage: 50 })).not.toThrow(); + }); + + it('should work with perPage being more than 100', () => { + expect(() => GetAgentPoliciesRequestSchema.query.validate({ perPage: 500 })).not.toThrow(); + }); + + it('should work with perPage being less than 100 and agentCount and full', () => { + expect(() => + GetAgentPoliciesRequestSchema.query.validate({ perPage: 50, full: true, noAgentCount: false }) + ).not.toThrow(); + }); + + it('should work with perPage being less than 100 and agentCount', () => { + expect(() => + GetAgentPoliciesRequestSchema.query.validate({ perPage: 50, noAgentCount: true }) + ).not.toThrow(); + }); + + it('should work with perPage being less than 100 and no agentCount and full', () => { + expect(() => + GetAgentPoliciesRequestSchema.query.validate({ perPage: 50, full: true, noAgentCount: false }) + ).not.toThrow(); + }); + + it('should throw with perPage being more than 100 and noagentCount', () => { + expect(() => + GetAgentPoliciesRequestSchema.query.validate({ + perPage: 500, + noAgentCount: false, + }) + ).toThrow(/perPage should be less or equal to 100 when fetching full policies or agent count./); + }); + + it('should throw with perPage being more than 100 and withAgentCount', () => { + expect(() => + GetAgentPoliciesRequestSchema.query.validate({ + perPage: 500, + withAgentCount: true, + }) + ).toThrow(/perPage should be less or equal to 100 when fetching full policies or agent count./); + }); + + it('should throw without perPage being more than 100 and full', () => { + expect(() => + GetAgentPoliciesRequestSchema.query.validate({ + perPage: 500, + noAgentCount: true, + full: true, + }) + ).toThrow(/perPage should be less or equal to 100 when fetching full policies or agent count./); + }); + + it('should not throw with perPage being more than 100 and no agentCount and no full', () => { + expect(() => + GetAgentPoliciesRequestSchema.query.validate({ + perPage: 500, + full: false, + noAgentCount: true, + }) + ).not.toThrow(); + }); +}); diff --git a/x-pack/plugins/fleet/server/types/rest_spec/agent_policy.ts b/x-pack/plugins/fleet/server/types/rest_spec/agent_policy.ts index c680c3477d836..6eb42468a6371 100644 --- a/x-pack/plugins/fleet/server/types/rest_spec/agent_policy.ts +++ b/x-pack/plugins/fleet/server/types/rest_spec/agent_policy.ts @@ -20,33 +20,59 @@ import { validateKuery } from '../../routes/utils/filter_utils'; import { BulkRequestBodySchema } from './common'; export const GetAgentPoliciesRequestSchema = { - query: schema.object({ - page: schema.maybe(schema.number({ defaultValue: 1 })), - perPage: schema.maybe(schema.number({ defaultValue: 20 })), - sortField: schema.maybe(schema.string()), - sortOrder: schema.maybe(schema.oneOf([schema.literal('desc'), schema.literal('asc')])), - showUpgradeable: schema.maybe(schema.boolean()), - kuery: schema.maybe( - schema.string({ - validate: (value: string) => { - const validationObj = validateKuery( - value, - [LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE], - AGENT_POLICY_MAPPINGS, - true - ); - if (validationObj?.error) { - return validationObj?.error; - } - }, - }) - ), - noAgentCount: schema.maybe(schema.boolean()), - full: schema.maybe(schema.boolean()), - format: schema.maybe( - schema.oneOf([schema.literal(inputsFormat.Simplified), schema.literal(inputsFormat.Legacy)]) - ), - }), + query: schema.object( + { + page: schema.maybe(schema.number({ defaultValue: 1 })), + perPage: schema.maybe(schema.number({ defaultValue: 20 })), + sortField: schema.maybe(schema.string()), + sortOrder: schema.maybe(schema.oneOf([schema.literal('desc'), schema.literal('asc')])), + showUpgradeable: schema.maybe(schema.boolean()), + kuery: schema.maybe( + schema.string({ + validate: (value: string) => { + const validationObj = validateKuery( + value, + [LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE], + AGENT_POLICY_MAPPINGS, + true + ); + if (validationObj?.error) { + return validationObj?.error; + } + }, + }) + ), + noAgentCount: schema.maybe( + schema.boolean({ + meta: { description: 'use withAgentCount instead', deprecated: true }, + }) + ), // + withAgentCount: schema.maybe( + schema.boolean({ + meta: { description: 'get policies with agent count' }, + }) + ), + full: schema.maybe( + schema.boolean({ + meta: { description: 'get full policies with package policies populated' }, + }) + ), + format: schema.maybe( + schema.oneOf([schema.literal(inputsFormat.Simplified), schema.literal(inputsFormat.Legacy)]) + ), + }, + { + validate: (query) => { + if ( + query.perPage && + query.perPage > 100 && + (query.full || query.noAgentCount === false || query.withAgentCount === true) + ) { + return 'perPage should be less or equal to 100 when fetching full policies or agent count.'; + } + }, + } + ), }; export const BulkGetAgentPoliciesRequestSchema = { @@ -145,3 +171,17 @@ export const GetK8sManifestRequestSchema = { export const GetK8sManifestResponseScheme = schema.object({ item: schema.string(), }); + +export const GetAgentPolicyOutputsRequestSchema = { + params: schema.object({ + agentPolicyId: schema.string(), + }), +}; + +export const GetListAgentPolicyOutputsRequestSchema = { + body: schema.object({ + ids: schema.arrayOf(schema.string(), { + meta: { description: 'list of package policy ids' }, + }), + }), +}; diff --git a/x-pack/plugins/global_search_bar/public/components/search_bar.tsx b/x-pack/plugins/global_search_bar/public/components/search_bar.tsx index efc564089fb43..de9bb85f7a8a3 100644 --- a/x-pack/plugins/global_search_bar/public/components/search_bar.tsx +++ b/x-pack/plugins/global_search_bar/public/components/search_bar.tsx @@ -177,7 +177,7 @@ export const SearchBar: FC = (opts) => { let tagIds: string[] | undefined; if (taggingApi && rawParams.filters.tags) { tagIds = rawParams.filters.tags.map( - (tagName) => taggingApi.ui.getTagIdFromName(tagName.toLowerCase()) ?? UNKNOWN_TAG_ID + (tagName) => taggingApi.ui.getTagIdFromName(tagName) ?? UNKNOWN_TAG_ID ); } else { tagIds = undefined; diff --git a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx index 84cb0fa027ad1..d73d95600e5b1 100644 --- a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx +++ b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx @@ -194,6 +194,7 @@ export const StepLogistics: React.FunctionComponent = React.memo( const { submit, isSubmitted, + isSubmitting, isValid: isFormValid, getErrors: getFormErrors, getFormData, @@ -275,7 +276,7 @@ export const StepLogistics: React.FunctionComponent = React.memo(
diff --git a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/edit_data_retention_modal/edit_data_retention_modal.tsx b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/edit_data_retention_modal/edit_data_retention_modal.tsx index ca3612bdab168..f747abca19f05 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/edit_data_retention_modal/edit_data_retention_modal.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/edit_data_retention_modal/edit_data_retention_modal.tsx @@ -23,6 +23,7 @@ import { has } from 'lodash'; import { ScopedHistory } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { isBiggerThanGlobalMaxRetention } from './validations'; import { useForm, useFormData, @@ -34,6 +35,7 @@ import { UseField, ToggleField, NumericField, + fieldValidators, } from '../../../../../shared_imports'; import { reactRouterNavigate } from '../../../../../shared_imports'; @@ -53,35 +55,6 @@ interface Props { onClose: (data?: { hasUpdatedDataRetention: boolean }) => void; } -const convertToMinutes = (value: string) => { - const { size, unit } = splitSizeAndUnits(value); - const sizeNum = parseInt(size, 10); - - switch (unit) { - case 'd': - // days to minutes - return sizeNum * 24 * 60; - case 'h': - // hours to minutes - return sizeNum * 60; - case 'm': - // minutes to minutes - return sizeNum; - case 's': - // seconds to minutes - return sizeNum / 60; - default: - throw new Error(`Unknown unit: ${unit}`); - } -}; - -const isRetentionBiggerThan = (valueA: string, valueB: string) => { - const minutesA = convertToMinutes(valueA); - const minutesB = convertToMinutes(valueB); - - return minutesA > minutesB; -}; - const configurationFormSchema: FormSchema = { dataRetention: { type: FIELD_TYPES.TEXT, @@ -94,50 +67,62 @@ const configurationFormSchema: FormSchema = { formatters: [fieldFormatters.toInt], validations: [ { - validator: ({ value, formData, customData }) => { - // If infiniteRetentionPeriod is set, we dont need to validate the data retention field - if (formData.infiniteRetentionPeriod) { - return undefined; + validator: ({ value }) => { + // TODO: Replace with validator added in https://github.com/elastic/kibana/pull/196527/ + if (!Number.isInteger(Number(value ?? ''))) { + return { + message: i18n.translate( + 'xpack.idxMgmt.dataStreamsDetailsPanel.editDataRetentionModal.dataRetentionFieldIntegerError', + { + defaultMessage: 'Only integers are allowed.', + } + ), + }; } - - // If project level data retention is enabled, we need to enforce the global max retention - const { globalMaxRetention, enableProjectLevelRetentionChecks } = customData.value as any; - if (enableProjectLevelRetentionChecks) { - const currentValue = `${value}${formData.timeUnit}`; - if (globalMaxRetention && isRetentionBiggerThan(currentValue, globalMaxRetention)) { - return { - message: i18n.translate( - 'xpack.idxMgmt.dataStreamsDetailsPanel.editDataRetentionModal.dataRetentionFieldMaxError', - { - defaultMessage: - 'Maximum data retention period on this project is {maxRetention} days.', - // Remove the unit from the globalMaxRetention value - values: { maxRetention: globalMaxRetention.slice(0, -1) }, - } - ), - }; + }, + }, + { + validator: ({ value, formData, customData }) => { + // We only need to validate the data retention field if infiniteRetentionPeriod is set to false + if (!formData.infiniteRetentionPeriod) { + // If project level data retention is enabled, we need to enforce the global max retention + const { globalMaxRetention, enableProjectLevelRetentionChecks } = + customData.value as any; + if (enableProjectLevelRetentionChecks) { + return isBiggerThanGlobalMaxRetention(value, formData.timeUnit, globalMaxRetention); } } - - if (!value) { - return { - message: i18n.translate( + }, + }, + { + validator: (args) => { + // We only need to validate the data retention field if infiniteRetentionPeriod is set to false + if (!args.formData.infiniteRetentionPeriod) { + return fieldValidators.emptyField( + i18n.translate( 'xpack.idxMgmt.dataStreamsDetailsPanel.editDataRetentionModal.dataRetentionFieldRequiredError', { defaultMessage: 'A data retention value is required.', } - ), - }; + ) + )(args); } - if (value <= 0) { - return { + }, + }, + { + validator: (args) => { + // We only need to validate the data retention field if infiniteRetentionPeriod is set to false + if (!args.formData.infiniteRetentionPeriod) { + return fieldValidators.numberGreaterThanField({ + than: 0, + allowEquality: false, message: i18n.translate( 'xpack.idxMgmt.dataStreamsDetailsPanel.editDataRetentionModal.dataRetentionFieldNonNegativeError', { defaultMessage: `A positive value is required.`, } ), - }; + })(args); } }, }, @@ -258,11 +243,11 @@ export const EditDataRetentionModal: React.FunctionComponent = ({ const formHasErrors = form.getErrors().length > 0; const disableSubmit = formHasErrors || !isDirty || form.isValid === false; - // Whenever the formData changes, we need to re-validate the dataRetention field - // as it depends on the timeUnit field. + // Whenever the timeUnit field changes, we need to re-validate + // the dataRetention field useEffect(() => { form.validateFields(['dataRetention']); - }, [formData, form]); + }, [formData.timeUnit, form]); const onSubmitForm = async () => { const { isValid, data } = await form.submit(); diff --git a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/edit_data_retention_modal/validations.test.ts b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/edit_data_retention_modal/validations.test.ts new file mode 100644 index 0000000000000..0768d0990cdc0 --- /dev/null +++ b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/edit_data_retention_modal/validations.test.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 { isBiggerThanGlobalMaxRetention } from './validations'; + +describe('isBiggerThanGlobalMaxRetention', () => { + it('should return undefined if any argument is missing', () => { + expect(isBiggerThanGlobalMaxRetention('', 'd', '30d')).toBeUndefined(); + expect(isBiggerThanGlobalMaxRetention(10, '', '30d')).toBeUndefined(); + expect(isBiggerThanGlobalMaxRetention(10, 'd', '')).toBeUndefined(); + }); + + it('should return an error message if retention is bigger than global max retention (in days)', () => { + const result = isBiggerThanGlobalMaxRetention(40, 'd', '30d'); + expect(result).toEqual({ + message: 'Maximum data retention period on this project is 30 days.', + }); + }); + + it('should return undefined if retention is smaller than or equal to global max retention (in days)', () => { + expect(isBiggerThanGlobalMaxRetention(30, 'd', '30d')).toBeUndefined(); + expect(isBiggerThanGlobalMaxRetention(25, 'd', '30d')).toBeUndefined(); + }); + + it('should correctly compare retention in different time units against days', () => { + expect(isBiggerThanGlobalMaxRetention(24, 'h', '1d')).toBeUndefined(); + expect(isBiggerThanGlobalMaxRetention(23, 'h', '1d')).toBeUndefined(); + // 30 days = 720 hours + expect(isBiggerThanGlobalMaxRetention(800, 'h', '30d')).toEqual({ + message: 'Maximum data retention period on this project is 30 days.', + }); + + // 1 day = 1440 minutes + expect(isBiggerThanGlobalMaxRetention(1440, 'm', '1d')).toBeUndefined(); + expect(isBiggerThanGlobalMaxRetention(3000, 'm', '2d')).toEqual({ + message: 'Maximum data retention period on this project is 2 days.', + }); + + // 1 day = 86400 seconds + expect(isBiggerThanGlobalMaxRetention(1000, 's', '1d')).toBeUndefined(); + expect(isBiggerThanGlobalMaxRetention(87000, 's', '1d')).toEqual({ + message: 'Maximum data retention period on this project is 1 days.', + }); + }); + + it('should throw an error for unknown time units', () => { + expect(() => isBiggerThanGlobalMaxRetention(10, 'x', '30d')).toThrow('Unknown unit: x'); + }); +}); diff --git a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/edit_data_retention_modal/validations.ts b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/edit_data_retention_modal/validations.ts new file mode 100644 index 0000000000000..831ac2f4c26b9 --- /dev/null +++ b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/edit_data_retention_modal/validations.ts @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { splitSizeAndUnits } from '../../../../../../common'; + +const convertToMinutes = (value: string) => { + const { size, unit } = splitSizeAndUnits(value); + const sizeNum = parseInt(size, 10); + + switch (unit) { + case 'd': + // days to minutes + return sizeNum * 24 * 60; + case 'h': + // hours to minutes + return sizeNum * 60; + case 'm': + // minutes to minutes + return sizeNum; + case 's': + // seconds to minutes (round up if any remainder) + return Math.ceil(sizeNum / 60); + default: + throw new Error(`Unknown unit: ${unit}`); + } +}; + +const isRetentionBiggerThan = (valueA: string, valueB: string) => { + const minutesA = convertToMinutes(valueA); + const minutesB = convertToMinutes(valueB); + + return minutesA > minutesB; +}; + +export const isBiggerThanGlobalMaxRetention = ( + retentionValue: string | number, + retentionTimeUnit: string, + globalMaxRetention: string +) => { + if (!retentionValue || !retentionTimeUnit || !globalMaxRetention) { + return undefined; + } + + return isRetentionBiggerThan(`${retentionValue}${retentionTimeUnit}`, globalMaxRetention) + ? { + message: i18n.translate( + 'xpack.idxMgmt.dataStreamsDetailsPanel.editDataRetentionModal.dataRetentionFieldMaxError', + { + defaultMessage: 'Maximum data retention period on this project is {maxRetention} days.', + // Remove the unit from the globalMaxRetention value + values: { maxRetention: globalMaxRetention.slice(0, -1) }, + } + ), + } + : undefined; +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/manage_processors/add_database_modal.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/manage_processors/add_database_modal.tsx index 6289fe3953f3e..075ff0b6a3665 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/sections/manage_processors/add_database_modal.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/manage_processors/add_database_modal.tsx @@ -137,7 +137,7 @@ export const AddDatabaseModal = ({ helpText={ } > @@ -156,7 +156,7 @@ export const AddDatabaseModal = ({ title={ } iconType="iInCircle" @@ -164,7 +164,7 @@ export const AddDatabaseModal = ({

@@ -178,7 +178,7 @@ export const AddDatabaseModal = ({ title={ } iconType="iInCircle" @@ -186,7 +186,7 @@ export const AddDatabaseModal = ({

@@ -246,7 +246,7 @@ export const AddDatabaseModal = ({

diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/manage_processors/empty_list.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/manage_processors/empty_list.tsx index d5e908b155feb..f021055dd2934 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/sections/manage_processors/empty_list.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/manage_processors/empty_list.tsx @@ -8,6 +8,7 @@ import { EuiPageTemplate } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import React from 'react'; +import { css } from '@emotion/react/dist/emotion-react.cjs'; export const EmptyList = ({ addDatabaseButton }: { addDatabaseButton: JSX.Element }) => { return ( @@ -26,11 +27,15 @@ export const EmptyList = ({ addDatabaseButton }: { addDatabaseButton: JSX.Elemen

} actions={addDatabaseButton} + css={css` + width: 450px; + padding: 0 20px 0 20px; + `} /> ); }; diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/manage_processors/geoip_list.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/manage_processors/geoip_list.tsx index e09ac4e6e2c4d..0acb66ba7dd25 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/sections/manage_processors/geoip_list.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/manage_processors/geoip_list.tsx @@ -162,7 +162,7 @@ export const GeoipList: React.FunctionComponent = () => {

diff --git a/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx b/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx index da99d693539b8..d26ce3f01cf34 100644 --- a/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx +++ b/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx @@ -1071,6 +1071,7 @@ export const LensTopNavMenu = ({ return ( ({ }), description: i18n.translate('xpack.lens.visTypeAlias.description', { defaultMessage: - 'Create visualizations with our drag and drop editor. Switch between visualization types at any time.', - }), - note: i18n.translate('xpack.lens.visTypeAlias.note', { - defaultMessage: 'Recommended for most users.', + 'Create visualizations using an intuitive drag-and-drop interface. Smart suggestions help you follow best practices and find the chart types that best match your data.', }), order: 60, icon: 'lensApp', diff --git a/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_suggestions.test.ts b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_suggestions.test.ts index 794c8cedf5164..0de7fc26608d2 100644 --- a/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_suggestions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_suggestions.test.ts @@ -171,7 +171,7 @@ describe('metric_suggestions', () => { expect(rest).toHaveLength(0); expect(suggestion).toMatchInlineSnapshot(` Object { - "hide": false, + "hide": true, "previewIcon": [Function], "score": 0.1, "state": Object { @@ -207,7 +207,7 @@ describe('metric_suggestions', () => { expect(rest).toHaveLength(0); expect(suggestion).toMatchInlineSnapshot(` Object { - "hide": false, + "hide": true, "previewIcon": [Function], "score": 0.1, "state": Object { diff --git a/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_suggestions.ts b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_suggestions.ts index 93e564cde841e..1b190cc0ce66f 100644 --- a/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_suggestions.ts +++ b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_suggestions.ts @@ -52,7 +52,7 @@ function getSuggestion( return { title, - hide: datasourceId === 'textBased', + hide: true, score: 0.1, previewIcon: IconChartMetric, state: { diff --git a/x-pack/plugins/lens/public/visualizations/metric/suggestions.test.ts b/x-pack/plugins/lens/public/visualizations/metric/suggestions.test.ts index fff365e0e89dd..4f1c7b46d06be 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/suggestions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/metric/suggestions.test.ts @@ -159,7 +159,7 @@ describe('metric suggestions', () => { // should ignore bucketed column for initial drag }, title: 'Metric', - hide: true, + hide: false, previewIcon: IconChartMetric, score: 0.51, }, @@ -189,7 +189,7 @@ describe('metric suggestions', () => { breakdownByAccessor: bucketColumn.columnId, }, title: 'Metric', - hide: true, + hide: false, previewIcon: IconChartMetric, score: 0.51, }, @@ -221,7 +221,7 @@ describe('metric suggestions', () => { breakdownByAccessor: bucketColumn.columnId, }, title: 'Metric', - hide: true, + hide: false, previewIcon: IconChartMetric, score: 0.51, }, @@ -294,7 +294,7 @@ describe('metric suggestions', () => { breakdownByAccessor: bucketColumn.columnId, }, title: 'Metric', - hide: true, + hide: false, previewIcon: IconChartMetric, score: 0.52, }, @@ -326,7 +326,7 @@ describe('metric suggestions', () => { breakdownByAccessor: bucketColumn.columnId, }, title: 'Metric', - hide: true, + hide: false, previewIcon: IconChartMetric, score: 0.52, }, @@ -357,7 +357,7 @@ describe('metric suggestions', () => { breakdownByAccessor: bucketColumn.columnId, }, title: 'Metric', - hide: true, + hide: false, previewIcon: IconChartMetric, score: 0.52, }, diff --git a/x-pack/plugins/lens/public/visualizations/metric/suggestions.ts b/x-pack/plugins/lens/public/visualizations/metric/suggestions.ts index b405ea646d980..877d2b45d66af 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/suggestions.ts +++ b/x-pack/plugins/lens/public/visualizations/metric/suggestions.ts @@ -30,6 +30,8 @@ export const getSuggestions: Visualization['getSuggest const bucketedColumns = table.columns.filter(({ operation }) => operation.isBucketed); + const hasInterval = bucketedColumns.some(({ operation }) => operation.scale === 'interval'); + const unsupportedColumns = table.columns.filter( ({ operation }) => !supportedDataTypes.has(operation.dataType) && !operation.isBucketed ); @@ -59,11 +61,10 @@ export const getSuggestions: Visualization['getSuggest layerId: table.layerId, layerType: LayerTypes.DATA, }, - title: metricLabel, + title: metricColumns[0]?.operation.label || metricLabel, previewIcon: IconChartMetric, score: 0.5, - // don't show suggestions since we're in tech preview - hide: true, + hide: hasInterval, }; const accessorMappings: Pick = @@ -72,7 +73,11 @@ export const getSuggestions: Visualization['getSuggest breakdownByAccessor: bucketedColumns[0]?.columnId, }; - baseSuggestion.score += 0.01 * Object.values(accessorMappings).filter(Boolean).length; + baseSuggestion.score = Number( + (baseSuggestion.score + 0.01 * Object.values(accessorMappings).filter(Boolean).length).toFixed( + 2 + ) + ); const suggestion = { ...baseSuggestion, diff --git a/x-pack/plugins/lens/public/visualizations/xy/to_expression.test.ts b/x-pack/plugins/lens/public/visualizations/xy/to_expression.test.ts index 08a013d036d5e..0571ca43f0bc7 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/to_expression.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/to_expression.test.ts @@ -121,31 +121,30 @@ describe('#toExpression', () => { ).toMatchSnapshot(); }); - it('should default the fitting function to None', () => { - expect( - ( - xyVisualization.toExpression( + it('should default the fitting function to Linear', () => { + const ast = xyVisualization.toExpression( + { + legend: { position: Position.Bottom, isVisible: true }, + valueLabels: 'hide', + preferredSeriesType: 'bar', + layers: [ { - legend: { position: Position.Bottom, isVisible: true }, - valueLabels: 'hide', - preferredSeriesType: 'bar', - layers: [ - { - layerId: 'first', - layerType: LayerTypes.DATA, - seriesType: 'area', - splitAccessor: 'd', - xAccessor: 'a', - accessors: ['b', 'c'], - }, - ], + layerId: 'first', + layerType: LayerTypes.DATA, + seriesType: 'area', + splitAccessor: 'd', + xAccessor: 'a', + accessors: ['b', 'c'], }, - frame.datasourceLayers, - undefined, - datasourceExpressionsByLayers - ) as Ast - ).chain[0].arguments.fittingFunction[0] - ).toEqual('None'); + ], + }, + frame.datasourceLayers, + undefined, + datasourceExpressionsByLayers + ) as Ast; + + expect(ast.chain[0].arguments.fittingFunction[0]).toEqual('Linear'); + expect(ast.chain[0].arguments.emphasizeFitting[0]).toEqual(true); }); it('should default the axisTitles visibility settings to true', () => { diff --git a/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts b/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts index c756c4eb137a9..b249624605791 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts @@ -35,6 +35,8 @@ import { XYCurveType, YAxisConfigFn, } from '@kbn/expression-xy-plugin/common'; + +import { FittingFunctions } from '@kbn/expression-xy-plugin/public'; import type { EventAnnotationConfig } from '@kbn/event-annotation-common'; import { LayerTypes } from '@kbn/expression-xy-plugin/public'; import { SystemPaletteExpressionFunctionDefinition } from '@kbn/charts-plugin/common'; @@ -336,9 +338,9 @@ export const buildXYExpression = ( const layeredXyVisFn = buildExpressionFunction('layeredXyVis', { legend: buildExpression([legendConfigFn]).toAst(), - fittingFunction: state.fittingFunction ?? 'None', + fittingFunction: state.fittingFunction ?? FittingFunctions.LINEAR, endValue: state.endValue ?? 'None', - emphasizeFitting: state.emphasizeFitting ?? false, + emphasizeFitting: state.emphasizeFitting ?? true, minBarHeight: state.minBarHeight ?? 1, fillOpacity: state.fillOpacity ?? 0.3, valueLabels: state.valueLabels ?? 'hide', diff --git a/x-pack/plugins/lens/public/visualizations/xy/types.ts b/x-pack/plugins/lens/public/visualizations/xy/types.ts index 694799b94638e..ee6575bfe0bbd 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/types.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/types.ts @@ -59,6 +59,8 @@ export const SeriesTypes = { BAR_HORIZONTAL_PERCENTAGE_STACKED: 'bar_horizontal_percentage_stacked', } as const; +export const defaultSeriesType = SeriesTypes.BAR_STACKED; + export type YAxisMode = $Values; export type SeriesType = $Values; export interface AxesSettingsConfig { diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 6f17a2253a35e..51f79bf58eeac 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -89,7 +89,6 @@ import { } from './annotations/helpers'; import { checkXAccessorCompatibility, - defaultSeriesType, getAnnotationLayerTitle, getAnnotationsLayers, getAxisName, @@ -112,6 +111,7 @@ import { } from './visualization_helpers'; import { getAxesConfiguration, groupAxesByType } from './axes_configuration'; import type { XYByValueAnnotationLayerConfig, XYState } from './types'; +import { defaultSeriesType } from './types'; import { defaultAnnotationLabel } from './annotations/helpers'; import { onDropForVisualization } from '../../editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils'; import { createAnnotationActions } from './annotations/actions'; diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx index 970671128aecf..09f6c0fe18cfd 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx @@ -270,7 +270,6 @@ export function getDescription(state?: State, layerId?: string) { } export const defaultIcon = IconChartBarStacked; -export const defaultSeriesType = 'bar_stacked'; export const supportedDataLayer = { type: layerTypes.DATA, diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/fitting_function_definitions.ts b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/fitting_function_definitions.ts index c2286a942baca..2ce2cd5f3cd68 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/fitting_function_definitions.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/fitting_function_definitions.ts @@ -7,10 +7,11 @@ import { i18n } from '@kbn/i18n'; import type { FittingFunction } from '@kbn/expression-xy-plugin/common'; +import { FittingFunctions } from '@kbn/expression-xy-plugin/public'; export const fittingFunctionDefinitions: Array<{ id: FittingFunction } & Record> = [ { - id: 'None', + id: FittingFunctions.NONE, title: i18n.translate('xpack.lens.fittingFunctionsTitle.none', { defaultMessage: 'Hide', }), @@ -19,7 +20,7 @@ export const fittingFunctionDefinitions: Array<{ id: FittingFunction } & Record< }), }, { - id: 'Zero', + id: FittingFunctions.ZERO, title: i18n.translate('xpack.lens.fittingFunctionsTitle.zero', { defaultMessage: 'Zero', }), @@ -28,7 +29,7 @@ export const fittingFunctionDefinitions: Array<{ id: FittingFunction } & Record< }), }, { - id: 'Linear', + id: FittingFunctions.LINEAR, title: i18n.translate('xpack.lens.fittingFunctionsTitle.linear', { defaultMessage: 'Linear', }), @@ -37,7 +38,7 @@ export const fittingFunctionDefinitions: Array<{ id: FittingFunction } & Record< }), }, { - id: 'Carry', + id: FittingFunctions.CARRY, title: i18n.translate('xpack.lens.fittingFunctionsTitle.carry', { defaultMessage: 'Last', }), @@ -46,7 +47,7 @@ export const fittingFunctionDefinitions: Array<{ id: FittingFunction } & Record< }), }, { - id: 'Lookahead', + id: FittingFunctions.LOOKAHEAD, title: i18n.translate('xpack.lens.fittingFunctionsTitle.lookahead', { defaultMessage: 'Next', }), diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/missing_values_option.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/missing_values_option.tsx index feda75599e4cc..c600f4fa4bae4 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/missing_values_option.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/missing_values_option.tsx @@ -9,6 +9,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFormRow, EuiIconTip, EuiSuperSelect, EuiSwitch, EuiText } from '@elastic/eui'; import type { FittingFunction, EndValue } from '@kbn/expression-xy-plugin/common'; +import { FittingFunctions } from '@kbn/expression-xy-plugin/public'; import { fittingFunctionDefinitions } from './fitting_function_definitions'; import { endValueDefinitions } from './end_value_definitions'; @@ -25,7 +26,7 @@ export interface MissingValuesOptionProps { export const MissingValuesOptions: React.FC = ({ onFittingFnChange, fittingFunction, - emphasizeFitting, + emphasizeFitting = true, onEmphasizeFittingChange, onEndValueChange, endValue, @@ -78,13 +79,13 @@ export const MissingValuesOptions: React.FC = ({ inputDisplay: title, }; })} - valueOfSelected={fittingFunction || 'None'} + valueOfSelected={fittingFunction || FittingFunctions.LINEAR} onChange={(value) => onFittingFnChange(value)} itemLayoutAlign="top" hasDividers /> - {fittingFunction && fittingFunction !== 'None' && ( + {fittingFunction && fittingFunction !== FittingFunctions.NONE && ( <> = ({ inputDisplay: title, }; })} - valueOfSelected={endValue || 'None'} + valueOfSelected={endValue || FittingFunctions.NONE} onChange={(value) => onEndValueChange(value)} itemLayoutAlign="top" hasDividers diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts index 77f79b5db6b1a..7ce86ed903065 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts @@ -633,7 +633,7 @@ describe('xy_suggestions', () => { legend: { isVisible: true, position: 'bottom' }, valueLabels: 'hide', preferredSeriesType: 'bar', - fittingFunction: 'None', + fittingFunction: 'Linear', layers: [ { accessors: ['price'], @@ -691,7 +691,7 @@ describe('xy_suggestions', () => { legend: { isVisible: true, position: 'bottom' }, valueLabels: 'hide', preferredSeriesType: 'bar', - fittingFunction: 'None', + fittingFunction: 'Linear', layers: [ { layerId: 'first', @@ -823,7 +823,7 @@ describe('xy_suggestions', () => { const currentState: XYState = { legend: { isVisible: true, position: 'bottom' }, valueLabels: 'hide', - fittingFunction: 'None', + fittingFunction: 'Linear', preferredSeriesType: 'line', layers: [ { @@ -854,11 +854,46 @@ describe('xy_suggestions', () => { expect((suggestions[0].state.layers[0] as XYDataLayerConfig).seriesType).toEqual('line'); }); - test('makes a visible seriesType suggestion for unchanged table without split', () => { + test('suggests line if changeType is initial and date column is involved', () => { const currentState: XYState = { legend: { isVisible: true, position: 'bottom' }, valueLabels: 'hide', fittingFunction: 'None', + preferredSeriesType: 'bar_stacked', + layers: [ + { + accessors: [], + layerId: 'first', + layerType: LayerTypes.DATA, + seriesType: 'bar_stacked', + splitAccessor: undefined, + xAccessor: '', + }, + ], + }; + const suggestions = getSuggestions({ + table: { + isMultiRow: true, + columns: [numCol('price'), dateCol('date')], + layerId: 'first', + changeType: 'initial', + }, + state: currentState, + keptLayerIds: ['first'], + }); + + expect(suggestions).toHaveLength(1); + + expect(suggestions[0].hide).toEqual(false); + expect(suggestions[0].state.preferredSeriesType).toEqual('line'); + expect((suggestions[0].state.layers[0] as XYDataLayerConfig).seriesType).toEqual('line'); + }); + + test('makes a visible seriesType suggestion for unchanged table without split', () => { + const currentState: XYState = { + legend: { isVisible: true, position: 'bottom' }, + valueLabels: 'hide', + fittingFunction: 'Linear', axisTitlesVisibilitySettings: { x: true, yLeft: true, yRight: true }, gridlinesVisibilitySettings: { x: true, yLeft: true, yRight: true }, tickLabelsVisibilitySettings: { x: true, yLeft: false, yRight: false }, @@ -908,7 +943,7 @@ describe('xy_suggestions', () => { legend: { isVisible: true, position: 'bottom' }, valueLabels: 'hide', preferredSeriesType: 'bar', - fittingFunction: 'None', + fittingFunction: 'Linear', axisTitlesVisibilitySettings: { x: true, yLeft: true, yRight: true }, gridlinesVisibilitySettings: { x: true, yLeft: true, yRight: true }, tickLabelsVisibilitySettings: { x: true, yLeft: false, yRight: false }, @@ -967,7 +1002,7 @@ describe('xy_suggestions', () => { const currentState: XYState = { legend: { isVisible: true, position: 'bottom' }, valueLabels: 'hide', - fittingFunction: 'None', + fittingFunction: 'Linear', preferredSeriesType: 'bar', layers: [ { @@ -1006,7 +1041,7 @@ describe('xy_suggestions', () => { legend: { isVisible: true, position: 'bottom' }, valueLabels: 'hide', preferredSeriesType: 'bar', - fittingFunction: 'None', + fittingFunction: 'Linear', layers: [ { accessors: ['price'], @@ -1043,7 +1078,7 @@ describe('xy_suggestions', () => { legend: { isVisible: true, position: 'bottom' }, valueLabels: 'hide', preferredSeriesType: 'bar', - fittingFunction: 'None', + fittingFunction: 'Linear', axisTitlesVisibilitySettings: { x: true, yLeft: true, yRight: true }, gridlinesVisibilitySettings: { x: true, yLeft: true, yRight: true }, tickLabelsVisibilitySettings: { x: true, yLeft: false, yRight: false }, @@ -1089,7 +1124,7 @@ describe('xy_suggestions', () => { legend: { isVisible: true, position: 'bottom' }, valueLabels: 'hide', preferredSeriesType: 'bar', - fittingFunction: 'None', + fittingFunction: 'Linear', axisTitlesVisibilitySettings: { x: true, yLeft: true, yRight: true }, gridlinesVisibilitySettings: { x: true, yLeft: true, yRight: true }, tickLabelsVisibilitySettings: { x: true, yLeft: false, yRight: false }, @@ -1136,7 +1171,7 @@ describe('xy_suggestions', () => { legend: { isVisible: true, position: 'bottom' }, valueLabels: 'hide', preferredSeriesType: 'bar', - fittingFunction: 'None', + fittingFunction: 'Linear', axisTitlesVisibilitySettings: { x: true, yLeft: true, yRight: true }, gridlinesVisibilitySettings: { x: true, yLeft: true, yRight: true }, tickLabelsVisibilitySettings: { x: true, yLeft: false, yRight: false }, diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.ts b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.ts index 0974e50ef36fe..5efaf4d8c949e 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.ts @@ -8,7 +8,8 @@ import { i18n } from '@kbn/i18n'; import { partition } from 'lodash'; import { Position } from '@elastic/charts'; -import { LayerTypes } from '@kbn/expression-xy-plugin/public'; +import { FittingFunctions, LayerTypes } from '@kbn/expression-xy-plugin/public'; + import type { SuggestionRequest, VisualizationSuggestion, @@ -24,6 +25,7 @@ import { XYLayerConfig, XYDataLayerConfig, SeriesType, + defaultSeriesType, } from './types'; import { flipSeriesType, getIconForSeries } from './state_helpers'; import { getDataLayers, isDataLayer } from './visualization_helpers'; @@ -100,21 +102,24 @@ function getSuggestionForColumns( allowMixed?: boolean ): VisualizationSuggestion | Array> | undefined { const [buckets, values] = partition(table.columns, (col) => col.operation.isBucketed); + const sharedArgs = { + layerId: table.layerId, + changeType: table.changeType, + currentState, + tableLabel: table.label, + keptLayerIds, + requestedSeriesType: seriesType, + mainPalette, + allowMixed, + }; if (buckets.length === 1 || buckets.length === 2) { - const [x, splitBy] = getBucketMappings(table, currentState); + const [xValue, splitBy] = getBucketMappings(table, currentState); return getSuggestionsForLayer({ - layerId: table.layerId, - changeType: table.changeType, - xValue: x, + ...sharedArgs, + xValue, yValues: values, splitBy, - currentState, - tableLabel: table.label, - keptLayerIds, - requestedSeriesType: seriesType, - mainPalette, - allowMixed, }); } else if (buckets.length === 0) { const [yValues, [xValue, splitBy]] = partition( @@ -122,17 +127,10 @@ function getSuggestionForColumns( (col) => col.operation.dataType === 'number' && !col.operation.isBucketed ); return getSuggestionsForLayer({ - layerId: table.layerId, - changeType: table.changeType, + ...sharedArgs, xValue, yValues, splitBy, - currentState, - tableLabel: table.label, - keptLayerIds, - requestedSeriesType: seriesType, - mainPalette, - allowMixed, }); } } @@ -235,6 +233,9 @@ function getSuggestionsForLayer({ allowMixed, }; + if (changeType === 'initial' && xValue?.operation.dataType === 'date') { + return buildSuggestion({ ...options, seriesType: 'line' }); + } // handles the simplest cases, acting as a chart switcher if (!currentState && changeType === 'unchanged') { // Chart switcher needs to include every chart type @@ -433,18 +434,16 @@ function getSeriesType( layerId: string, xValue?: TableSuggestionColumn ): SeriesType { - const defaultType = 'bar_stacked'; - const oldLayer = getExistingLayer(currentState, layerId); const oldLayerSeriesType = oldLayer && isDataLayer(oldLayer) ? oldLayer.seriesType : false; const closestSeriesType = - oldLayerSeriesType || (currentState && currentState.preferredSeriesType) || defaultType; + oldLayerSeriesType || (currentState && currentState.preferredSeriesType) || defaultSeriesType; // Attempt to keep the seriesType consistent on initial add of a layer // Ordinal scales should always use a bar because there is no interpolation between buckets if (xValue && xValue.operation.scale && xValue.operation.scale === 'ordinal') { - return closestSeriesType.startsWith('bar') ? closestSeriesType : defaultType; + return closestSeriesType.startsWith('bar') ? closestSeriesType : defaultSeriesType; } return closestSeriesType; @@ -573,7 +572,7 @@ function buildSuggestion({ const state: State = { legend: currentState ? currentState.legend : { isVisible: true, position: Position.Right }, valueLabels: currentState?.valueLabels || 'hide', - fittingFunction: currentState?.fittingFunction || 'None', + fittingFunction: currentState?.fittingFunction ?? FittingFunctions.LINEAR, curveType: currentState?.curveType, fillOpacity: currentState?.fillOpacity, xTitle: currentState?.xTitle, diff --git a/x-pack/plugins/ml/server/routes/system.ts b/x-pack/plugins/ml/server/routes/system.ts index 0804a8dc02348..bf4fa3161f5b9 100644 --- a/x-pack/plugins/ml/server/routes/system.ts +++ b/x-pack/plugins/ml/server/routes/system.ts @@ -202,8 +202,9 @@ export function systemRoutes( options: { tags: ['access:ml:canGetJobs'], }, - deprecated: true, summary: 'ES Search wrapper', + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} + deprecated: true, }) .addVersion( { diff --git a/x-pack/plugins/observability_solution/apm/public/components/routing/app_root/apm_header_action_menu/labs/labs_flyout.tsx b/x-pack/plugins/observability_solution/apm/public/components/routing/app_root/apm_header_action_menu/labs/labs_flyout.tsx index 7eea2faf9f62c..bda1c7e4ee022 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/routing/app_root/apm_header_action_menu/labs/labs_flyout.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/routing/app_root/apm_header_action_menu/labs/labs_flyout.tsx @@ -20,6 +20,7 @@ import { EuiSpacer, EuiText, EuiTitle, + EuiToolTip, } from '@elastic/eui'; import { withSuspense } from '@kbn/shared-ux-utility'; import { i18n } from '@kbn/i18n'; @@ -42,7 +43,11 @@ interface Props { export function LabsFlyout({ onClose }: Props) { const trackApmEvent = useUiTracker({ app: 'apm' }); - const { docLinks, notifications } = useApmPluginContext().core; + const { docLinks, notifications, application } = useApmPluginContext().core; + + const canSave = + application.capabilities.advancedSettings.save && + (application.capabilities.apm['settings:save'] as boolean); const { data, status } = useFetcher( (callApmApi) => callApmApi('GET /internal/apm/settings/labs'), @@ -152,7 +157,7 @@ export function LabsFlyout({ onClose }: Props) { > @@ -172,16 +177,27 @@ export function LabsFlyout({ onClose }: Props) { - - {i18n.translate('xpack.apm.labs.reload', { - defaultMessage: 'Reload to apply changes', - })} - + + {i18n.translate('xpack.apm.labs.reload', { + defaultMessage: 'Reload to apply changes', + })} + + diff --git a/x-pack/plugins/observability_solution/dataset_quality/public/hooks/use_redirect_link.ts b/x-pack/plugins/observability_solution/dataset_quality/public/hooks/use_redirect_link.ts index 638af464a87cd..d1e55d488ba5c 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/public/hooks/use_redirect_link.ts +++ b/x-pack/plugins/observability_solution/dataset_quality/public/hooks/use_redirect_link.ts @@ -5,8 +5,12 @@ * 2.0. */ +import { map } from 'rxjs'; import { useMemo } from 'react'; +import useObservable from 'react-use/lib/useObservable'; +import { AppStatus } from '@kbn/core-application-browser'; import { + OBSERVABILITY_LOGS_EXPLORER_APP_ID, SINGLE_DATASET_LOCATOR_ID, SingleDatasetLocatorParams, } from '@kbn/deeplinks-observability'; @@ -34,7 +38,7 @@ export const useRedirectLink = ({ sendTelemetry: SendTelemetryFn; }) => { const { - services: { share }, + services: { share, application }, } = useKibanaContextForPlugin(); const { from, to } = timeRangeConfig; @@ -42,12 +46,24 @@ export const useRedirectLink = ({ const logsExplorerLocator = share.url.locators.get(SINGLE_DATASET_LOCATOR_ID); + const isLogsExplorerAppAccessible = useObservable( + application.applications$.pipe( + map( + (apps) => + (apps.get(OBSERVABILITY_LOGS_EXPLORER_APP_ID)?.status ?? AppStatus.inaccessible) === + AppStatus.accessible + ) + ), + false + ); + return useMemo<{ linkProps: RouterLinkProps; navigate: () => void; isLogsExplorerAvailable: boolean; }>(() => { - const isLogsExplorerAvailable = !!logsExplorerLocator && dataStreamStat.type === 'logs'; + const isLogsExplorerAvailable = + isLogsExplorerAppAccessible && !!logsExplorerLocator && dataStreamStat.type === 'logs'; const config = isLogsExplorerAvailable ? buildLogsExplorerConfig({ locator: logsExplorerLocator, @@ -95,6 +111,7 @@ export const useRedirectLink = ({ query, sendTelemetry, share.url.locators, + isLogsExplorerAppAccessible, ]); }; diff --git a/x-pack/plugins/observability_solution/dataset_quality/tsconfig.json b/x-pack/plugins/observability_solution/dataset_quality/tsconfig.json index 934c0e434d9a5..f0d82fadb54ad 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/tsconfig.json +++ b/x-pack/plugins/observability_solution/dataset_quality/tsconfig.json @@ -59,7 +59,8 @@ "@kbn/telemetry-plugin", "@kbn/usage-collection-plugin", "@kbn/rison", - "@kbn/task-manager-plugin" + "@kbn/task-manager-plugin", + "@kbn/core-application-browser" ], "exclude": [ "target/**/*" diff --git a/x-pack/plugins/observability_solution/inventory/.storybook/get_mock_inventory_context.tsx b/x-pack/plugins/observability_solution/inventory/.storybook/get_mock_inventory_context.tsx index 52ec669a9a75c..9c2ea13cf753e 100644 --- a/x-pack/plugins/observability_solution/inventory/.storybook/get_mock_inventory_context.tsx +++ b/x-pack/plugins/observability_solution/inventory/.storybook/get_mock_inventory_context.tsx @@ -12,8 +12,10 @@ import type { EntityManagerPublicPluginStart } from '@kbn/entityManager-plugin/p import type { InferencePublicStart } from '@kbn/inference-plugin/public'; import type { ObservabilitySharedPluginStart } from '@kbn/observability-shared-plugin/public'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; -import type { SharePluginStart } from '@kbn/share-plugin/public'; +import type { LocatorPublic, SharePluginStart } from '@kbn/share-plugin/public'; import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; +import type { HttpStart } from '@kbn/core-http-browser'; +import { action } from '@storybook/addon-actions'; import type { InventoryKibanaContext } from '../public/hooks/use_kibana'; import type { ITelemetryClient } from '../public/services/telemetry/types'; @@ -25,7 +27,21 @@ export function getMockInventoryContext(): InventoryKibanaContext { entityManager: {} as unknown as EntityManagerPublicPluginStart, observabilityShared: {} as unknown as ObservabilitySharedPluginStart, inference: {} as unknown as InferencePublicStart, - share: {} as unknown as SharePluginStart, + share: { + url: { + locators: { + get: (_id: string) => + ({ + navigate: async () => { + return Promise.resolve(); + }, + getRedirectUrl: (args: any) => { + action('share.url.locators.getRedirectUrl')(args); + }, + } as unknown as LocatorPublic), + }, + }, + } as unknown as SharePluginStart, telemetry: {} as unknown as ITelemetryClient, unifiedSearch: {} as unknown as UnifiedSearchPublicPluginStart, dataViews: {} as unknown as DataViewsPublicPluginStart, @@ -34,6 +50,13 @@ export function getMockInventoryContext(): InventoryKibanaContext { fetch: jest.fn(), stream: jest.fn(), }, + http: { + basePath: { + prepend: (_path: string) => { + return ''; + }, + }, + } as unknown as HttpStart, spaces: {} as unknown as SpacesPluginStart, kibanaEnvironment: { isCloudEnv: false, diff --git a/x-pack/plugins/observability_solution/inventory/.storybook/jest_setup.js b/x-pack/plugins/observability_solution/inventory/.storybook/main.ts similarity index 62% rename from x-pack/plugins/observability_solution/inventory/.storybook/jest_setup.js rename to x-pack/plugins/observability_solution/inventory/.storybook/main.ts index 32071b8aa3f62..bf63e08d64c32 100644 --- a/x-pack/plugins/observability_solution/inventory/.storybook/jest_setup.js +++ b/x-pack/plugins/observability_solution/inventory/.storybook/main.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { setGlobalConfig } from '@storybook/testing-react'; -import * as globalStorybookConfig from './preview'; +import { defaultConfig } from '@kbn/storybook'; -setGlobalConfig(globalStorybookConfig); +module.exports = defaultConfig; diff --git a/x-pack/plugins/observability_solution/inventory/.storybook/preview.js b/x-pack/plugins/observability_solution/inventory/.storybook/preview.tsx similarity index 63% rename from x-pack/plugins/observability_solution/inventory/.storybook/preview.js rename to x-pack/plugins/observability_solution/inventory/.storybook/preview.tsx index c8155e9c3d92c..9bcd37d60628a 100644 --- a/x-pack/plugins/observability_solution/inventory/.storybook/preview.js +++ b/x-pack/plugins/observability_solution/inventory/.storybook/preview.tsx @@ -5,9 +5,11 @@ * 2.0. */ -import { EuiThemeProviderDecorator } from '@kbn/kibana-react-plugin/common'; +import { addDecorator } from '@storybook/react'; import * as jest from 'jest-mock'; +import { KibanaReactStorybookDecorator } from './storybook_decorator'; +// @ts-ignore window.jest = jest; -export const decorators = [EuiThemeProviderDecorator]; +addDecorator(KibanaReactStorybookDecorator); diff --git a/x-pack/plugins/observability_solution/inventory/.storybook/storybook_decorator.tsx b/x-pack/plugins/observability_solution/inventory/.storybook/storybook_decorator.tsx index 20e507e54b5d5..8c98289608d92 100644 --- a/x-pack/plugins/observability_solution/inventory/.storybook/storybook_decorator.tsx +++ b/x-pack/plugins/observability_solution/inventory/.storybook/storybook_decorator.tsx @@ -4,15 +4,12 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { ComponentType, useMemo } from 'react'; +import React, { useMemo } from 'react'; +import { DecoratorFn } from '@storybook/react'; import { InventoryContextProvider } from '../public/context/inventory_context_provider'; import { getMockInventoryContext } from './get_mock_inventory_context'; -export function KibanaReactStorybookDecorator(Story: ComponentType) { +export const KibanaReactStorybookDecorator: DecoratorFn = (story) => { const context = useMemo(() => getMockInventoryContext(), []); - return ( - - - - ); -} + return {story()}; +}; diff --git a/x-pack/plugins/observability_solution/inventory/jest.config.js b/x-pack/plugins/observability_solution/inventory/jest.config.js index 4e4450567243c..4fd85ffa49368 100644 --- a/x-pack/plugins/observability_solution/inventory/jest.config.js +++ b/x-pack/plugins/observability_solution/inventory/jest.config.js @@ -13,9 +13,6 @@ module.exports = { '/x-pack/plugins/observability_solution/inventory/common', '/x-pack/plugins/observability_solution/inventory/server', ], - setupFiles: [ - '/x-pack/plugins/observability_solution/inventory/.storybook/jest_setup.js', - ], collectCoverage: true, collectCoverageFrom: [ '/x-pack/plugins/observability_solution/inventory/{public,common,server}/**/*.{js,ts,tsx}', diff --git a/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/entities_grid.stories.tsx b/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/entities_grid.stories.tsx index 1f4d3f1f34b40..a89781ad2742a 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/entities_grid.stories.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/entities_grid.stories.tsx @@ -5,51 +5,69 @@ * 2.0. */ -import { EuiDataGridSorting, EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui'; +import { EuiButton, EuiDataGridSorting, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { Meta, Story } from '@storybook/react'; import { orderBy } from 'lodash'; import React, { useMemo, useState } from 'react'; import { ENTITY_LAST_SEEN, ENTITY_TYPE } from '@kbn/observability-shared-plugin/common'; +import { useArgs } from '@storybook/addons'; import { EntitiesGrid } from '.'; import { EntityType } from '../../../common/entities'; import { entitiesMock } from './mock/entities_mock'; -const stories: Meta<{}> = { +interface EntityGridStoriesArgs { + entityType?: EntityType; +} + +const entityTypeOptions: EntityType[] = ['host', 'container', 'service']; + +const stories: Meta = { title: 'app/inventory/entities_grid', component: EntitiesGrid, + argTypes: { + entityType: { + options: entityTypeOptions, + name: 'Entity type', + control: { + type: 'select', + }, + }, + }, + args: { entityType: undefined }, }; -export default stories; -export const Example: Story<{}> = () => { +export const Grid: Story = (args) => { const [pageIndex, setPageIndex] = useState(0); + const [{ entityType }, updateArgs] = useArgs(); const [sort, setSort] = useState({ id: ENTITY_LAST_SEEN, direction: 'desc', }); - const [selectedEntityType, setSelectedEntityType] = useState(); const filteredAndSortedItems = useMemo( () => orderBy( - selectedEntityType - ? entitiesMock.filter((mock) => mock[ENTITY_TYPE] === selectedEntityType) - : entitiesMock, + entityType ? entitiesMock.filter((mock) => mock[ENTITY_TYPE] === entityType) : entitiesMock, sort.id, sort.direction ), - [selectedEntityType, sort.direction, sort.id] + [entityType, sort.direction, sort.id] ); return ( - {`Entity filter: ${selectedEntityType || 'N/A'}`} - setSelectedEntityType(undefined)} - > - Clear filter - + + {`Entity filter: ${entityType || 'N/A'}`} + + updateArgs({ entityType: undefined })} + > + Clear filter + + + = () => { onChangePage={setPageIndex} onChangeSort={setSort} pageIndex={pageIndex} - onFilterByType={setSelectedEntityType} + onFilterByType={(selectedEntityType) => updateArgs({ entityType: selectedEntityType })} /> ); }; -export const EmptyGridExample: Story<{}> = () => { +export const EmptyGrid: Story = (args) => { const [pageIndex, setPageIndex] = useState(0); const [sort, setSort] = useState({ id: ENTITY_LAST_SEEN, @@ -87,3 +105,5 @@ export const EmptyGridExample: Story<{}> = () => { /> ); }; + +export default stories; diff --git a/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/mock/entities_mock.ts b/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/mock/entities_mock.ts index bf72d5d7832cf..8a34a9f68c7b6 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/mock/entities_mock.ts +++ b/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/mock/entities_mock.ts @@ -5,3016 +5,66 @@ * 2.0. */ -import { APIReturnType } from '../../../api'; +import { faker } from '@faker-js/faker'; +import { + ENTITY_DISPLAY_NAME, + ENTITY_TYPE, + ENTITY_ID, + ENTITY_LAST_SEEN, +} from '@kbn/observability-shared-plugin/common'; +import { Entity, EntityType } from '../../../../common/entities'; -type InventoryEntitiesAPIReturnType = APIReturnType<'GET /internal/inventory/entities'>; +const idGenerator = () => { + let id = 0; + return () => (++id).toString(); +}; -export const entitiesMock = [ +const generateId = idGenerator(); + +function generateRandomTimestamp() { + const end = new Date(); + const start = new Date(end); + + start.setHours(start.getHours() - 24); + const randomDate = new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime())); + + return randomDate.toISOString(); +} + +const getEntity = (entityType: EntityType) => ({ + [ENTITY_LAST_SEEN]: generateRandomTimestamp(), + [ENTITY_TYPE]: entityType, + [ENTITY_DISPLAY_NAME]: faker.person.fullName(), + [ENTITY_ID]: generateId(), +}); + +const alertsMock = [ { - 'entity.lastSeenTimestamp': '2023-08-20T10:50:06.384Z', - 'entity.type': 'host', - 'entity.displayName': 'Spider-Man', - 'entity.id': '0', + ...getEntity('host'), alertsCount: 3, }, { - 'entity.lastSeenTimestamp': '2024-06-16T21:48:16.259Z', - 'entity.type': 'service', - 'entity.displayName': 'Iron Man', - 'entity.id': '1', + ...getEntity('service'), alertsCount: 3, }, { - 'entity.lastSeenTimestamp': '2024-04-28T03:31:57.528Z', - 'entity.type': 'host', - 'entity.displayName': 'Captain America', - 'entity.id': '2', + ...getEntity('host'), alertsCount: 10, }, { - 'entity.lastSeenTimestamp': '2024-05-14T11:32:04.275Z', - 'entity.type': 'host', - 'entity.displayName': 'Hulk', - 'entity.id': '3', + ...getEntity('host'), alertsCount: 1, }, - { - 'entity.lastSeenTimestamp': '2023-12-05T13:33:54.028Z', - 'entity.type': 'container', - 'entity.displayName': 'Thor', - 'entity.id': '4', - }, - { - 'entity.lastSeenTimestamp': '2023-11-27T06:18:52.650Z', - 'entity.type': 'service', - 'entity.displayName': 'Black Widow', - 'entity.id': '5', - }, - { - 'entity.lastSeenTimestamp': '2023-06-23T21:20:41.460Z', - 'entity.type': 'container', - 'entity.displayName': 'Batman', - 'entity.id': '6', - }, - { - 'entity.lastSeenTimestamp': '2023-12-08T03:23:09.317Z', - 'entity.type': 'service', - 'entity.displayName': 'Superman', - 'entity.id': '7', - }, - { - 'entity.lastSeenTimestamp': '2024-08-07T19:06:52.169Z', - 'entity.type': 'service', - 'entity.displayName': 'Wonder Woman', - 'entity.id': '8', - }, - { - 'entity.lastSeenTimestamp': '2024-08-15T01:15:23.589Z', - 'entity.type': 'container', - 'entity.displayName': 'Aquaman', - 'entity.id': '9', - }, - { - 'entity.lastSeenTimestamp': '2024-05-18T09:44:35.799Z', - 'entity.type': 'service', - 'entity.displayName': 'Flash', - 'entity.id': '10', - }, - { - 'entity.lastSeenTimestamp': '2023-12-20T19:12:29.251Z', - 'entity.type': 'container', - 'entity.displayName': 'Cyborg', - 'entity.id': '11', - }, - { - 'entity.lastSeenTimestamp': '2024-04-04T02:52:28.431Z', - 'entity.type': 'container', - 'entity.displayName': 'Wolverine', - 'entity.id': '12', - }, - { - 'entity.lastSeenTimestamp': '2023-07-14T05:13:12.906Z', - 'entity.type': 'host', - 'entity.displayName': 'Deadpool', - 'entity.id': '13', - }, - { - 'entity.lastSeenTimestamp': '2023-07-21T07:30:55.389Z', - 'entity.type': 'service', - 'entity.displayName': 'Green Lantern', - 'entity.id': '14', - }, - { - 'entity.lastSeenTimestamp': '2024-06-16T09:30:32.331Z', - 'entity.type': 'service', - 'entity.displayName': 'Doctor Strange', - 'entity.id': '15', - }, - { - 'entity.lastSeenTimestamp': '2023-08-24T08:05:46.687Z', - 'entity.type': 'container', - 'entity.displayName': 'Ant-Man', - 'entity.id': '16', - }, - { - 'entity.lastSeenTimestamp': '2024-03-23T09:37:36.874Z', - 'entity.type': 'service', - 'entity.displayName': 'Scarlet Witch', - 'entity.id': '17', - }, - { - 'entity.lastSeenTimestamp': '2023-05-12T02:34:46.188Z', - 'entity.type': 'host', - 'entity.displayName': 'Black Panther', - 'entity.id': '18', - }, - { - 'entity.lastSeenTimestamp': '2023-01-05T07:16:17.213Z', - 'entity.type': 'container', - 'entity.displayName': 'Captain Marvel', - 'entity.id': '19', - }, - { - 'entity.lastSeenTimestamp': '2024-05-28T04:08:43.047Z', - 'entity.type': 'host', - 'entity.displayName': 'Hawkeye', - 'entity.id': '20', - }, - { - 'entity.lastSeenTimestamp': '2024-04-23T02:01:01.149Z', - 'entity.type': 'service', - 'entity.displayName': 'Vision', - 'entity.id': '21', - }, - { - 'entity.lastSeenTimestamp': '2023-04-08T10:40:14.658Z', - 'entity.type': 'host', - 'entity.displayName': 'Shazam', - 'entity.id': '22', - }, - { - 'entity.lastSeenTimestamp': '2024-01-11T09:03:11.465Z', - 'entity.type': 'service', - 'entity.displayName': 'Nightwing', - 'entity.id': '23', - }, - { - 'entity.lastSeenTimestamp': '2024-04-27T22:35:18.822Z', - 'entity.type': 'container', - 'entity.displayName': 'Robin', - 'entity.id': '24', - }, - { - 'entity.lastSeenTimestamp': '2023-03-09T22:05:08.071Z', - 'entity.type': 'container', - 'entity.displayName': 'Starfire', - 'entity.id': '25', - }, - { - 'entity.lastSeenTimestamp': '2024-08-09T13:20:31.960Z', - 'entity.type': 'service', - 'entity.displayName': 'Beast Boy', - 'entity.id': '26', - }, - { - 'entity.lastSeenTimestamp': '2024-07-12T01:44:33.204Z', - 'entity.type': 'service', - 'entity.displayName': 'Raven', - 'entity.id': '27', - }, - { - 'entity.lastSeenTimestamp': '2023-01-31T00:08:53.817Z', - 'entity.type': 'service', - 'entity.displayName': 'Daredevil', - 'entity.id': '28', - }, - { - 'entity.lastSeenTimestamp': '2024-03-26T08:37:11.019Z', - 'entity.type': 'container', - 'entity.displayName': 'Luke Cage', - 'entity.id': '29', - }, - { - 'entity.lastSeenTimestamp': '2023-05-17T08:49:09.112Z', - 'entity.type': 'service', - 'entity.displayName': 'Jessica Jones', - 'entity.id': '30', - }, - { - 'entity.lastSeenTimestamp': '2024-06-15T20:05:12.395Z', - 'entity.type': 'service', - 'entity.displayName': 'Punisher', - 'entity.id': '31', - }, - { - 'entity.lastSeenTimestamp': '2024-07-30T06:53:16.477Z', - 'entity.type': 'service', - 'entity.displayName': 'Groot', - 'entity.id': '32', - }, - { - 'entity.lastSeenTimestamp': '2024-06-01T13:22:53.973Z', - 'entity.type': 'host', - 'entity.displayName': 'Rocket Raccoon', - 'entity.id': '33', - }, - { - 'entity.lastSeenTimestamp': '2024-09-12T17:44:12.492Z', - 'entity.type': 'container', - 'entity.displayName': 'Gamora', - 'entity.id': '34', - }, - { - 'entity.lastSeenTimestamp': '2024-03-28T13:44:52.732Z', - 'entity.type': 'service', - 'entity.displayName': 'Drax', - 'entity.id': '35', - }, - { - 'entity.lastSeenTimestamp': '2023-09-19T01:20:23.901Z', - 'entity.type': 'container', - 'entity.displayName': 'Mantis', - 'entity.id': '36', - }, - { - 'entity.lastSeenTimestamp': '2023-01-17T07:04:52.387Z', - 'entity.type': 'service', - 'entity.displayName': 'Winter Soldier', - 'entity.id': '37', - }, - { - 'entity.lastSeenTimestamp': '2023-10-07T15:08:39.776Z', - 'entity.type': 'host', - 'entity.displayName': 'Falcon', - 'entity.id': '38', - }, - { - 'entity.lastSeenTimestamp': '2024-05-01T17:45:43.595Z', - 'entity.type': 'host', - 'entity.displayName': 'Silver Surfer', - 'entity.id': '39', - }, - { - 'entity.lastSeenTimestamp': '2023-01-12T19:33:15.526Z', - 'entity.type': 'host', - 'entity.displayName': 'Moon Knight', - 'entity.id': '40', - }, - { - 'entity.lastSeenTimestamp': '2023-03-28T23:24:20.896Z', - 'entity.type': 'container', - 'entity.displayName': 'She-Hulk', - 'entity.id': '41', - }, - { - 'entity.lastSeenTimestamp': '2023-03-15T09:52:58.134Z', - 'entity.type': 'container', - 'entity.displayName': 'Blade', - 'entity.id': '42', - }, - { - 'entity.lastSeenTimestamp': '2023-04-18T07:38:32.158Z', - 'entity.type': 'container', - 'entity.displayName': 'Ghost Rider', - 'entity.id': '43', - }, - { - 'entity.lastSeenTimestamp': '2024-03-16T16:36:47.704Z', - 'entity.type': 'host', - 'entity.displayName': 'Cyclops', - 'entity.id': '44', - }, - { - 'entity.lastSeenTimestamp': '2023-06-11T13:40:02.951Z', - 'entity.type': 'service', - 'entity.displayName': 'Jean Grey', - 'entity.id': '45', - }, - { - 'entity.lastSeenTimestamp': '2024-09-11T23:54:53.129Z', - 'entity.type': 'container', - 'entity.displayName': 'Storm', - 'entity.id': '46', - }, - { - 'entity.lastSeenTimestamp': '2024-03-31T15:26:58.694Z', - 'entity.type': 'host', - 'entity.displayName': 'Iceman', - 'entity.id': '47', - }, - { - 'entity.lastSeenTimestamp': '2023-01-15T05:36:56.655Z', - 'entity.type': 'host', - 'entity.displayName': 'Colossus', - 'entity.id': '48', - }, - { - 'entity.lastSeenTimestamp': '2024-06-01T22:59:08.883Z', - 'entity.type': 'service', - 'entity.displayName': 'Kitty Pryde', - 'entity.id': '49', - }, - { - 'entity.lastSeenTimestamp': '2024-04-16T21:38:10.398Z', - 'entity.type': 'container', - 'entity.displayName': 'Psylocke', - 'entity.id': '50', - }, - { - 'entity.lastSeenTimestamp': '2023-02-13T07:41:37.539Z', - 'entity.type': 'container', - 'entity.displayName': 'Rogue', - 'entity.id': '51', - }, - { - 'entity.lastSeenTimestamp': '2023-12-11T14:40:29.422Z', - 'entity.type': 'service', - 'entity.displayName': 'Professor X', - 'entity.id': '52', - }, - { - 'entity.lastSeenTimestamp': '2023-03-06T09:50:33.183Z', - 'entity.type': 'host', - 'entity.displayName': 'Magneto', - 'entity.id': '53', - }, - { - 'entity.lastSeenTimestamp': '2024-06-30T14:52:19.840Z', - 'entity.type': 'host', - 'entity.displayName': 'Quicksilver', - 'entity.id': '54', - }, - { - 'entity.lastSeenTimestamp': '2023-08-16T01:03:06.855Z', - 'entity.type': 'container', - 'entity.displayName': 'Scarlet Witch', - 'entity.id': '55', - }, - { - 'entity.lastSeenTimestamp': '2023-12-19T23:23:08.821Z', - 'entity.type': 'host', - 'entity.displayName': 'Black Bolt', - 'entity.id': '56', - }, - { - 'entity.lastSeenTimestamp': '2024-01-04T06:04:23.837Z', - 'entity.type': 'service', - 'entity.displayName': 'Medusa', - 'entity.id': '57', - }, - { - 'entity.lastSeenTimestamp': '2024-01-02T11:03:36.265Z', - 'entity.type': 'container', - 'entity.displayName': 'Crystal', - 'entity.id': '58', - }, - { - 'entity.lastSeenTimestamp': '2023-01-14T04:12:51.710Z', - 'entity.type': 'service', - 'entity.displayName': 'Karnak', - 'entity.id': '59', - }, - { - 'entity.lastSeenTimestamp': '2023-09-16T15:31:25.215Z', - 'entity.type': 'container', - 'entity.displayName': 'Gorgon', - 'entity.id': '60', - }, - { - 'entity.lastSeenTimestamp': '2023-03-19T23:21:32.571Z', - 'entity.type': 'container', - 'entity.displayName': 'Triton', - 'entity.id': '61', - }, - { - 'entity.lastSeenTimestamp': '2024-02-08T21:57:35.600Z', - 'entity.type': 'host', - 'entity.displayName': 'Lockjaw', - 'entity.id': '62', - }, - { - 'entity.lastSeenTimestamp': '2024-02-26T03:18:43.161Z', - 'entity.type': 'container', - 'entity.displayName': 'Namor', - 'entity.id': '63', - }, - { - 'entity.lastSeenTimestamp': '2024-03-13T13:39:54.430Z', - 'entity.type': 'host', - 'entity.displayName': 'Hercules', - 'entity.id': '64', - }, - { - 'entity.lastSeenTimestamp': '2024-06-15T15:57:15.557Z', - 'entity.type': 'host', - 'entity.displayName': 'Valkyrie', - 'entity.id': '65', - }, - { - 'entity.lastSeenTimestamp': '2023-09-14T15:29:09.268Z', - 'entity.type': 'host', - 'entity.displayName': 'Sif', - 'entity.id': '66', - }, - { - 'entity.lastSeenTimestamp': '2023-06-06T11:32:45.998Z', - 'entity.type': 'service', - 'entity.displayName': 'Heimdall', - 'entity.id': '67', - }, - { - 'entity.lastSeenTimestamp': '2023-06-23T20:19:29.918Z', - 'entity.type': 'container', - 'entity.displayName': 'Loki', - 'entity.id': '68', - }, - { - 'entity.lastSeenTimestamp': '2024-02-15T19:08:56.703Z', - 'entity.type': 'service', - 'entity.displayName': 'Odin', - 'entity.id': '69', - }, - { - 'entity.lastSeenTimestamp': '2024-05-05T21:13:36.761Z', - 'entity.type': 'host', - 'entity.displayName': 'Enchantress', - 'entity.id': '70', - }, - { - 'entity.lastSeenTimestamp': '2023-07-29T20:51:41.023Z', - 'entity.type': 'container', - 'entity.displayName': 'Executioner', - 'entity.id': '71', - }, - { - 'entity.lastSeenTimestamp': '2023-08-06T17:17:53.101Z', - 'entity.type': 'container', - 'entity.displayName': 'Balder', - 'entity.id': '72', - }, - { - 'entity.lastSeenTimestamp': '2023-07-03T05:18:36.705Z', - 'entity.type': 'container', - 'entity.displayName': 'Beta Ray Bill', - 'entity.id': '73', - }, - { - 'entity.lastSeenTimestamp': '2023-05-26T14:32:39.569Z', - 'entity.type': 'container', - 'entity.displayName': 'Adam Warlock', - 'entity.id': '74', - }, - { - 'entity.lastSeenTimestamp': '2023-04-22T20:19:48.018Z', - 'entity.type': 'host', - 'entity.displayName': 'Ego the Living Planet', - 'entity.id': '75', - }, - { - 'entity.lastSeenTimestamp': '2024-09-01T05:03:37.465Z', - 'entity.type': 'container', - 'entity.displayName': 'Ronan the Accuser', - 'entity.id': '76', - }, - { - 'entity.lastSeenTimestamp': '2024-03-30T18:51:01.608Z', - 'entity.type': 'service', - 'entity.displayName': 'Nebula', - 'entity.id': '77', - }, - { - 'entity.lastSeenTimestamp': '2023-08-03T00:46:22.909Z', - 'entity.type': 'container', - 'entity.displayName': 'Yondu', - 'entity.id': '78', - }, - { - 'entity.lastSeenTimestamp': '2024-03-22T19:27:42.105Z', - 'entity.type': 'container', - 'entity.displayName': 'Star-Lord', - 'entity.id': '79', - }, - { - 'entity.lastSeenTimestamp': '2023-03-01T12:52:43.009Z', - 'entity.type': 'service', - 'entity.displayName': 'Elektra', - 'entity.id': '80', - }, - { - 'entity.lastSeenTimestamp': '2024-03-01T03:35:49.365Z', - 'entity.type': 'container', - 'entity.displayName': 'Bullseye', - 'entity.id': '81', - }, - { - 'entity.lastSeenTimestamp': '2023-04-23T03:29:05.951Z', - 'entity.type': 'service', - 'entity.displayName': 'Kingpin', - 'entity.id': '82', - }, - { - 'entity.lastSeenTimestamp': '2023-08-19T14:56:49.093Z', - 'entity.type': 'container', - 'entity.displayName': 'Iron Fist', - 'entity.id': '83', - }, - { - 'entity.lastSeenTimestamp': '2023-04-17T09:03:32.311Z', - 'entity.type': 'service', - 'entity.displayName': 'Misty Knight', - 'entity.id': '84', - }, - { - 'entity.lastSeenTimestamp': '2024-06-23T06:42:12.471Z', - 'entity.type': 'service', - 'entity.displayName': 'Colleen Wing', - 'entity.id': '85', - }, - { - 'entity.lastSeenTimestamp': '2023-10-20T10:59:37.573Z', - 'entity.type': 'host', - 'entity.displayName': 'Shang-Chi', - 'entity.id': '86', - }, - { - 'entity.lastSeenTimestamp': '2024-01-18T10:07:55.134Z', - 'entity.type': 'host', - 'entity.displayName': 'Black Cat', - 'entity.id': '87', - }, - { - 'entity.lastSeenTimestamp': '2024-09-04T14:02:31.795Z', - 'entity.type': 'container', - 'entity.displayName': 'Silver Sable', - 'entity.id': '88', - }, - { - 'entity.lastSeenTimestamp': '2023-09-21T16:08:59.195Z', - 'entity.type': 'container', - 'entity.displayName': 'Spider-Woman', - 'entity.id': '89', - }, - { - 'entity.lastSeenTimestamp': '2024-07-12T00:22:45.521Z', - 'entity.type': 'container', - 'entity.displayName': 'Dr. Nick', - 'entity.id': '90', - }, - { - 'entity.lastSeenTimestamp': '2023-06-27T20:43:47.331Z', - 'entity.type': 'container', - 'entity.displayName': 'Miles Morales', - 'entity.id': '91', - }, - { - 'entity.lastSeenTimestamp': '2023-11-15T05:35:28.421Z', - 'entity.type': 'host', - 'entity.displayName': 'Spider-Girl', - 'entity.id': '92', - }, - { - 'entity.lastSeenTimestamp': '2023-07-17T13:28:37.477Z', - 'entity.type': 'container', - 'entity.displayName': 'Nova', - 'entity.id': '93', - }, - { - 'entity.lastSeenTimestamp': '2024-05-13T09:58:21.185Z', - 'entity.type': 'container', - 'entity.displayName': 'Quasar', - 'entity.id': '94', - }, - { - 'entity.lastSeenTimestamp': '2023-09-22T18:29:20.589Z', - 'entity.type': 'container', - 'entity.displayName': 'Mar-Vell', - 'entity.id': '95', - }, - { - 'entity.lastSeenTimestamp': '2024-04-29T21:33:36.318Z', - 'entity.type': 'container', - 'entity.displayName': 'Monica Rambeau', - 'entity.id': '96', - }, - { - 'entity.lastSeenTimestamp': '2024-01-10T17:12:02.785Z', - 'entity.type': 'host', - 'entity.displayName': 'Photon', - 'entity.id': '97', - }, - { - 'entity.lastSeenTimestamp': '2024-08-03T04:59:46.730Z', - 'entity.type': 'container', - 'entity.displayName': 'Blue Marvel', - 'entity.id': '98', - }, - { - 'entity.lastSeenTimestamp': '2023-04-22T05:48:54.665Z', - 'entity.type': 'host', - 'entity.displayName': 'Sentry', - 'entity.id': '99', - }, - { - 'entity.lastSeenTimestamp': '2024-05-08T05:53:56.652Z', - 'entity.type': 'host', - 'entity.displayName': 'Hyperion', - 'entity.id': '100', - }, - { - 'entity.lastSeenTimestamp': '2024-08-21T08:45:38.667Z', - 'entity.type': 'service', - 'entity.displayName': 'Nighthawk', - 'entity.id': '101', - }, - { - 'entity.lastSeenTimestamp': '2024-08-15T14:03:39.798Z', - 'entity.type': 'host', - 'entity.displayName': 'Power Princess', - 'entity.id': '102', - }, - { - 'entity.lastSeenTimestamp': '2024-05-01T13:28:15.225Z', - 'entity.type': 'service', - 'entity.displayName': 'Doctor Spectrum', - 'entity.id': '103', - }, - { - 'entity.lastSeenTimestamp': '2023-01-21T21:03:45.309Z', - 'entity.type': 'container', - 'entity.displayName': 'Speed Demon', - 'entity.id': '104', - }, - { - 'entity.lastSeenTimestamp': '2023-03-29T06:15:14.140Z', - 'entity.type': 'container', - 'entity.displayName': 'Whizzer', - 'entity.id': '105', - }, - { - 'entity.lastSeenTimestamp': '2024-01-25T09:23:14.336Z', - 'entity.type': 'container', - 'entity.displayName': 'Scarlet Spider', - 'entity.id': '106', - }, - { - 'entity.lastSeenTimestamp': '2023-08-07T16:59:31.739Z', - 'entity.type': 'host', - 'entity.displayName': 'Kaine', - 'entity.id': '107', - }, - { - 'entity.lastSeenTimestamp': '2024-03-11T20:29:44.832Z', - 'entity.type': 'host', - 'entity.displayName': 'Ben Reilly', - 'entity.id': '108', - }, - { - 'entity.lastSeenTimestamp': '2023-05-08T00:40:17.226Z', - 'entity.type': 'service', - 'entity.displayName': 'Spider-Man 2099', - 'entity.id': '109', - }, - { - 'entity.lastSeenTimestamp': '2023-01-13T19:15:54.781Z', - 'entity.type': 'service', - 'entity.displayName': 'Spider-Ham', - 'entity.id': '110', - }, - { - 'entity.lastSeenTimestamp': '2024-09-02T15:35:26.309Z', - 'entity.type': 'container', - 'entity.displayName': 'Ultimate Spider-Man', - 'entity.id': '111', - }, - { - 'entity.lastSeenTimestamp': '2023-06-04T16:08:36.902Z', - 'entity.type': 'container', - 'entity.displayName': 'Spider-Man Noir', - 'entity.id': '112', - }, - { - 'entity.lastSeenTimestamp': '2023-02-12T13:28:29.732Z', - 'entity.type': 'service', - 'entity.displayName': 'Superior Spider-Man', - 'entity.id': '113', - }, - { - 'entity.lastSeenTimestamp': '2023-08-16T08:54:36.219Z', - 'entity.type': 'service', - 'entity.displayName': 'Agent Venom', - 'entity.id': '114', - }, - { - 'entity.lastSeenTimestamp': '2023-02-23T12:58:57.715Z', - 'entity.type': 'container', - 'entity.displayName': 'Venom', - 'entity.id': '115', - }, - { - 'entity.lastSeenTimestamp': '2023-06-19T18:17:35.424Z', - 'entity.type': 'container', - 'entity.displayName': 'Carnage', - 'entity.id': '116', - }, - { - 'entity.lastSeenTimestamp': '2024-05-02T11:58:44.239Z', - 'entity.type': 'service', - 'entity.displayName': 'Toxin', - 'entity.id': '117', - }, - { - 'entity.lastSeenTimestamp': '2023-12-27T14:15:59.641Z', - 'entity.type': 'host', - 'entity.displayName': 'Anti-Venom', - 'entity.id': '118', - }, - { - 'entity.lastSeenTimestamp': '2024-01-10T15:23:44.536Z', - 'entity.type': 'container', - 'entity.displayName': 'Morbius', - 'entity.id': '119', - }, - { - 'entity.lastSeenTimestamp': '2023-11-26T01:04:11.090Z', - 'entity.type': 'service', - 'entity.displayName': 'Kraven the Hunter', - 'entity.id': '120', - }, - { - 'entity.lastSeenTimestamp': '2024-02-21T04:11:13.221Z', - 'entity.type': 'container', - 'entity.displayName': 'The Lizard', - 'entity.id': '121', - }, - { - 'entity.lastSeenTimestamp': '2023-12-31T07:29:14.344Z', - 'entity.type': 'service', - 'entity.displayName': 'Sandman', - 'entity.id': '122', - }, - { - 'entity.lastSeenTimestamp': '2024-06-02T11:20:40.793Z', - 'entity.type': 'host', - 'entity.displayName': 'Rhino', - 'entity.id': '123', - }, - { - 'entity.lastSeenTimestamp': '2023-04-02T14:31:44.296Z', - 'entity.type': 'host', - 'entity.displayName': 'Shocker', - 'entity.id': '124', - }, - { - 'entity.lastSeenTimestamp': '2024-06-10T12:26:05.411Z', - 'entity.type': 'container', - 'entity.displayName': 'Vulture', - 'entity.id': '125', - }, - { - 'entity.lastSeenTimestamp': '2023-06-27T16:17:19.611Z', - 'entity.type': 'container', - 'entity.displayName': 'Mysterio', - 'entity.id': '126', - }, - { - 'entity.lastSeenTimestamp': '2023-08-29T04:54:25.898Z', - 'entity.type': 'service', - 'entity.displayName': 'Scorpion', - 'entity.id': '127', - }, - { - 'entity.lastSeenTimestamp': '2023-01-17T21:39:41.265Z', - 'entity.type': 'host', - 'entity.displayName': 'Chameleon', - 'entity.id': '128', - }, - { - 'entity.lastSeenTimestamp': '2023-06-07T03:03:11.032Z', - 'entity.type': 'host', - 'entity.displayName': 'Green Goblin', - 'entity.id': '129', - }, - { - 'entity.lastSeenTimestamp': '2023-05-19T19:18:21.005Z', - 'entity.type': 'service', - 'entity.displayName': 'Hobgoblin', - 'entity.id': '130', - }, - { - 'entity.lastSeenTimestamp': '2023-08-03T20:45:51.404Z', - 'entity.type': 'host', - 'entity.displayName': 'Demogoblin', - 'entity.id': '131', - }, - { - 'entity.lastSeenTimestamp': '2024-01-11T06:14:51.570Z', - 'entity.type': 'service', - 'entity.displayName': 'Red Goblin', - 'entity.id': '132', - }, - { - 'entity.lastSeenTimestamp': '2024-03-27T11:07:02.657Z', - 'entity.type': 'host', - 'entity.displayName': 'Doctor Octopus', - 'entity.id': '133', - }, - { - 'entity.lastSeenTimestamp': '2023-08-17T08:42:02.024Z', - 'entity.type': 'container', - 'entity.displayName': 'Electro', - 'entity.id': '134', - }, - { - 'entity.lastSeenTimestamp': '2023-07-02T16:02:17.438Z', - 'entity.type': 'container', - 'entity.displayName': 'Kingpin', - 'entity.id': '135', - }, - { - 'entity.lastSeenTimestamp': '2024-05-17T22:14:53.375Z', - 'entity.type': 'host', - 'entity.displayName': 'Tombstone', - 'entity.id': '136', - }, - { - 'entity.lastSeenTimestamp': '2023-05-30T09:26:45.647Z', - 'entity.type': 'service', - 'entity.displayName': 'Hammerhead', - 'entity.id': '137', - }, - { - 'entity.lastSeenTimestamp': '2024-09-08T03:21:22.494Z', - 'entity.type': 'host', - 'entity.displayName': 'Silvermane', - 'entity.id': '138', - }, - { - 'entity.lastSeenTimestamp': '2023-06-26T06:23:45.305Z', - 'entity.type': 'host', - 'entity.displayName': 'Hydro-Man', - 'entity.id': '139', - }, - { - 'entity.lastSeenTimestamp': '2024-08-15T13:29:01.603Z', - 'entity.type': 'host', - 'entity.displayName': 'Molten Man', - 'entity.id': '140', - }, - { - 'entity.lastSeenTimestamp': '2023-06-21T04:25:12.371Z', - 'entity.type': 'container', - 'entity.displayName': 'Morlun', - 'entity.id': '141', - }, - { - 'entity.lastSeenTimestamp': '2023-11-01T02:59:06.998Z', - 'entity.type': 'host', - 'entity.displayName': 'The Jackal', - 'entity.id': '142', - }, - { - 'entity.lastSeenTimestamp': '2023-06-25T15:27:39.801Z', - 'entity.type': 'service', - 'entity.displayName': 'Alistair Smythe', - 'entity.id': '143', - }, - { - 'entity.lastSeenTimestamp': '2023-12-07T19:13:02.711Z', - 'entity.type': 'service', - 'entity.displayName': 'The Beetle', - 'entity.id': '144', - }, - { - 'entity.lastSeenTimestamp': '2024-04-13T14:16:24.875Z', - 'entity.type': 'host', - 'entity.displayName': 'The Prowler', - 'entity.id': '145', - }, - { - 'entity.lastSeenTimestamp': '2023-11-02T20:25:05.117Z', - 'entity.type': 'host', - 'entity.displayName': 'Tarantula', - 'entity.id': '146', - }, - { - 'entity.lastSeenTimestamp': '2023-04-12T19:09:48.881Z', - 'entity.type': 'service', - 'entity.displayName': 'Black Tarantula', - 'entity.id': '147', - }, - { - 'entity.lastSeenTimestamp': '2024-01-25T01:37:16.115Z', - 'entity.type': 'host', - 'entity.displayName': 'White Tiger', - 'entity.id': '148', - }, - { - 'entity.lastSeenTimestamp': '2023-12-20T12:27:21.819Z', - 'entity.type': 'service', - 'entity.displayName': 'Nightcrawler', - 'entity.id': '149', - }, - { - 'entity.lastSeenTimestamp': '2024-06-11T05:30:01.226Z', - 'entity.type': 'container', - 'entity.displayName': 'Bishop', - 'entity.id': '150', - }, - { - 'entity.lastSeenTimestamp': '2023-09-24T00:18:40.137Z', - 'entity.type': 'service', - 'entity.displayName': 'Cable', - 'entity.id': '151', - }, - { - 'entity.lastSeenTimestamp': '2024-04-24T03:28:16.162Z', - 'entity.type': 'host', - 'entity.displayName': 'Domino', - 'entity.id': '152', - }, - { - 'entity.lastSeenTimestamp': '2023-04-08T07:23:33.921Z', - 'entity.type': 'host', - 'entity.displayName': 'Warpath', - 'entity.id': '153', - }, - { - 'entity.lastSeenTimestamp': '2023-04-12T23:26:45.533Z', - 'entity.type': 'service', - 'entity.displayName': 'Sunspot', - 'entity.id': '154', - }, - { - 'entity.lastSeenTimestamp': '2024-05-18T14:28:01.751Z', - 'entity.type': 'container', - 'entity.displayName': 'Cannonball', - 'entity.id': '155', - }, - { - 'entity.lastSeenTimestamp': '2023-03-14T17:08:06.243Z', - 'entity.type': 'container', - 'entity.displayName': 'Wolfsbane', - 'entity.id': '156', - }, - { - 'entity.lastSeenTimestamp': '2024-02-25T23:18:49.867Z', - 'entity.type': 'service', - 'entity.displayName': 'Magik', - 'entity.id': '157', - }, - { - 'entity.lastSeenTimestamp': '2024-07-14T14:31:58.080Z', - 'entity.type': 'container', - 'entity.displayName': 'Colossus', - 'entity.id': '158', - }, - { - 'entity.lastSeenTimestamp': '2023-05-09T22:32:41.723Z', - 'entity.type': 'container', - 'entity.displayName': 'Omega Red', - 'entity.id': '159', - }, - { - 'entity.lastSeenTimestamp': '2023-05-21T10:33:50.732Z', - 'entity.type': 'host', - 'entity.displayName': 'Juggernaut', - 'entity.id': '160', - }, - { - 'entity.lastSeenTimestamp': '2024-05-01T07:27:51.647Z', - 'entity.type': 'host', - 'entity.displayName': 'Sebastian Shaw', - 'entity.id': '161', - }, - { - 'entity.lastSeenTimestamp': '2024-01-25T09:47:54.565Z', - 'entity.type': 'service', - 'entity.displayName': 'Emma Frost', - 'entity.id': '162', - }, - { - 'entity.lastSeenTimestamp': '2023-10-25T15:51:18.513Z', - 'entity.type': 'host', - 'entity.displayName': 'Mystique', - 'entity.id': '163', - }, - { - 'entity.lastSeenTimestamp': '2023-03-27T07:26:04.804Z', - 'entity.type': 'service', - 'entity.displayName': 'Sabretooth', - 'entity.id': '164', - }, - { - 'entity.lastSeenTimestamp': '2024-07-22T15:29:51.446Z', - 'entity.type': 'host', - 'entity.displayName': 'Pyro', - 'entity.id': '165', - }, - { - 'entity.lastSeenTimestamp': '2024-06-26T09:09:57.169Z', - 'entity.type': 'host', - 'entity.displayName': 'Avalanche', - 'entity.id': '166', - }, - { - 'entity.lastSeenTimestamp': '2023-10-27T05:14:15.279Z', - 'entity.type': 'container', - 'entity.displayName': 'Destiny', - 'entity.id': '167', - }, - { - 'entity.lastSeenTimestamp': '2023-03-08T00:40:52.990Z', - 'entity.type': 'service', - 'entity.displayName': 'Forge', - 'entity.id': '168', - }, - { - 'entity.lastSeenTimestamp': '2023-11-05T16:40:30.510Z', - 'entity.type': 'host', - 'entity.displayName': 'Polaris', - 'entity.id': '169', - }, - { - 'entity.lastSeenTimestamp': '2024-08-12T05:04:27.632Z', - 'entity.type': 'service', - 'entity.displayName': 'Havok', - 'entity.id': '170', - }, - { - 'entity.lastSeenTimestamp': '2023-01-29T18:30:34.000Z', - 'entity.type': 'service', - 'entity.displayName': 'Multiple Man', - 'entity.id': '171', - }, - { - 'entity.lastSeenTimestamp': '2023-12-18T13:11:26.940Z', - 'entity.type': 'service', - 'entity.displayName': 'Strong Guy', - 'entity.id': '172', - }, - { - 'entity.lastSeenTimestamp': '2024-01-07T16:44:23.323Z', - 'entity.type': 'container', - 'entity.displayName': 'Feral', - 'entity.id': '173', - }, - { - 'entity.lastSeenTimestamp': '2023-03-19T22:38:34.493Z', - 'entity.type': 'container', - 'entity.displayName': 'Boom Boom', - 'entity.id': '174', - }, - { - 'entity.lastSeenTimestamp': '2023-05-31T07:23:57.500Z', - 'entity.type': 'container', - 'entity.displayName': 'Warlock', - 'entity.id': '175', - }, - { - 'entity.lastSeenTimestamp': '2024-07-11T01:57:10.851Z', - 'entity.type': 'host', - 'entity.displayName': 'Magus', - 'entity.id': '176', - }, - { - 'entity.lastSeenTimestamp': '2024-05-22T12:50:09.849Z', - 'entity.type': 'container', - 'entity.displayName': 'Blink', - 'entity.id': '177', - }, - { - 'entity.lastSeenTimestamp': '2023-10-20T11:56:09.004Z', - 'entity.type': 'service', - 'entity.displayName': 'Nocturne', - 'entity.id': '178', - }, - { - 'entity.lastSeenTimestamp': '2023-02-08T06:47:37.958Z', - 'entity.type': 'host', - 'entity.displayName': 'Morph', - 'entity.id': '179', - }, - { - 'entity.lastSeenTimestamp': '2023-09-16T01:14:58.701Z', - 'entity.type': 'host', - 'entity.displayName': 'Sunfire', - 'entity.id': '180', - }, - { - 'entity.lastSeenTimestamp': '2023-12-05T19:56:38.483Z', - 'entity.type': 'service', - 'entity.displayName': 'Thunderbird', - 'entity.id': '181', - }, - { - 'entity.lastSeenTimestamp': '2024-01-21T04:49:41.995Z', - 'entity.type': 'host', - 'entity.displayName': 'Banshee', - 'entity.id': '182', - }, - { - 'entity.lastSeenTimestamp': '2023-10-19T02:58:03.939Z', - 'entity.type': 'container', - 'entity.displayName': 'Syrin', - 'entity.id': '183', - }, - { - 'entity.lastSeenTimestamp': '2023-05-21T14:13:08.847Z', - 'entity.type': 'host', - 'entity.displayName': 'Moira MacTaggert', - 'entity.id': '184', - }, - { - 'entity.lastSeenTimestamp': '2024-02-09T05:57:59.984Z', - 'entity.type': 'container', - 'entity.displayName': 'Angel', - 'entity.id': '185', - }, - { - 'entity.lastSeenTimestamp': '2024-05-05T04:42:36.419Z', - 'entity.type': 'service', - 'entity.displayName': 'Archangel', - 'entity.id': '186', - }, - { - 'entity.lastSeenTimestamp': '2024-02-10T11:42:58.833Z', - 'entity.type': 'service', - 'entity.displayName': 'Iceman', - 'entity.id': '187', - }, - { - 'entity.lastSeenTimestamp': '2024-07-25T19:55:46.838Z', - 'entity.type': 'host', - 'entity.displayName': 'Beast', - 'entity.id': '188', - }, - { - 'entity.lastSeenTimestamp': '2024-09-11T05:07:10.339Z', - 'entity.type': 'host', - 'entity.displayName': 'Nightcrawler', - 'entity.id': '189', - }, - { - 'entity.lastSeenTimestamp': '2023-10-19T15:59:49.360Z', - 'entity.type': 'service', - 'entity.displayName': 'Phoenix', - 'entity.id': '190', - }, - { - 'entity.lastSeenTimestamp': '2024-09-07T17:32:26.019Z', - 'entity.type': 'host', - 'entity.displayName': 'X-Man', - 'entity.id': '191', - }, - { - 'entity.lastSeenTimestamp': '2023-07-13T12:49:11.603Z', - 'entity.type': 'container', - 'entity.displayName': 'Cable', - 'entity.id': '192', - }, - { - 'entity.lastSeenTimestamp': '2024-05-19T21:32:30.970Z', - 'entity.type': 'service', - 'entity.displayName': 'Deadpool', - 'entity.id': '193', - }, - { - 'entity.lastSeenTimestamp': '2023-12-12T00:33:27.870Z', - 'entity.type': 'host', - 'entity.displayName': 'Domino', - 'entity.id': '194', - }, - { - 'entity.lastSeenTimestamp': '2023-08-26T18:34:55.709Z', - 'entity.type': 'host', - 'entity.displayName': 'Shatterstar', - 'entity.id': '195', - }, - { - 'entity.lastSeenTimestamp': '2024-08-05T13:02:27.932Z', - 'entity.type': 'service', - 'entity.displayName': 'Warpath', - 'entity.id': '196', - }, - { - 'entity.lastSeenTimestamp': '2023-08-08T08:09:37.053Z', - 'entity.type': 'service', - 'entity.displayName': 'Rictor', - 'entity.id': '197', - }, - { - 'entity.lastSeenTimestamp': '2024-07-18T17:17:22.628Z', - 'entity.type': 'service', - 'entity.displayName': 'Boom Boom', - 'entity.id': '198', - }, - { - 'entity.lastSeenTimestamp': '2023-06-19T20:45:15.240Z', - 'entity.type': 'host', - 'entity.displayName': 'Magik', - 'entity.id': '199', - }, - { - 'entity.lastSeenTimestamp': '2023-07-29T15:18:44.936Z', - 'entity.type': 'container', - 'entity.displayName': 'Cannonball', - 'entity.id': '200', - }, - { - 'entity.lastSeenTimestamp': '2023-02-08T01:26:18.603Z', - 'entity.type': 'host', - 'entity.displayName': 'Sunspot', - 'entity.id': '201', - }, - { - 'entity.lastSeenTimestamp': '2023-02-22T16:06:39.387Z', - 'entity.type': 'service', - 'entity.displayName': 'Banshee', - 'entity.id': '202', - }, - { - 'entity.lastSeenTimestamp': '2023-04-27T03:32:37.015Z', - 'entity.type': 'host', - 'entity.displayName': 'Thunderbird', - 'entity.id': '203', - }, - { - 'entity.lastSeenTimestamp': '2023-09-08T13:07:04.895Z', - 'entity.type': 'service', - 'entity.displayName': 'X-23', - 'entity.id': '204', - }, - { - 'entity.lastSeenTimestamp': '2024-02-08T06:28:33.208Z', - 'entity.type': 'container', - 'entity.displayName': 'Daken', - 'entity.id': '205', - }, - { - 'entity.lastSeenTimestamp': '2024-01-19T19:28:19.416Z', - 'entity.type': 'host', - 'entity.displayName': 'Laura Kinney', - 'entity.id': '206', - }, - { - 'entity.lastSeenTimestamp': '2024-01-29T07:33:26.920Z', - 'entity.type': 'service', - 'entity.displayName': 'Jubilee', - 'entity.id': '207', - }, - { - 'entity.lastSeenTimestamp': '2023-02-20T10:19:34.322Z', - 'entity.type': 'host', - 'entity.displayName': 'Stepford Cuckoos', - 'entity.id': '208', - }, - { - 'entity.lastSeenTimestamp': '2024-06-03T03:31:08.704Z', - 'entity.type': 'service', - 'entity.displayName': 'Fantomex', - 'entity.id': '209', - }, - { - 'entity.lastSeenTimestamp': '2023-10-30T18:18:12.254Z', - 'entity.type': 'container', - 'entity.displayName': 'Marrow', - 'entity.id': '210', - }, - { - 'entity.lastSeenTimestamp': '2024-03-19T23:47:02.611Z', - 'entity.type': 'service', - 'entity.displayName': 'Pixie', - 'entity.id': '211', - }, - { - 'entity.lastSeenTimestamp': '2023-08-08T06:03:05.326Z', - 'entity.type': 'host', - 'entity.displayName': 'Armor', - 'entity.id': '212', - }, - { - 'entity.lastSeenTimestamp': '2023-04-05T11:25:37.426Z', - 'entity.type': 'service', - 'entity.displayName': 'Gentle', - 'entity.id': '213', - }, - { - 'entity.lastSeenTimestamp': '2023-01-10T22:18:30.812Z', - 'entity.type': 'container', - 'entity.displayName': 'Anole', - 'entity.id': '214', - }, - { - 'entity.lastSeenTimestamp': '2024-07-17T06:09:51.763Z', - 'entity.type': 'host', - 'entity.displayName': 'Rockslide', - 'entity.id': '215', - }, - { - 'entity.lastSeenTimestamp': '2024-02-02T00:44:56.270Z', - 'entity.type': 'host', - 'entity.displayName': 'Dust', - 'entity.id': '216', - }, - { - 'entity.lastSeenTimestamp': '2023-03-09T19:37:54.235Z', - 'entity.type': 'host', - 'entity.displayName': 'Mercury', - 'entity.id': '217', - }, - { - 'entity.lastSeenTimestamp': '2024-06-14T09:51:29.579Z', - 'entity.type': 'service', - 'entity.displayName': 'Surge', - 'entity.id': '218', - }, - { - 'entity.lastSeenTimestamp': '2024-03-12T17:28:48.254Z', - 'entity.type': 'host', - 'entity.displayName': 'Hellion', - 'entity.id': '219', - }, - { - 'entity.lastSeenTimestamp': '2023-04-09T07:19:02.429Z', - 'entity.type': 'service', - 'entity.displayName': 'Elixir', - 'entity.id': '220', - }, - { - 'entity.lastSeenTimestamp': '2024-05-10T08:28:21.025Z', - 'entity.type': 'host', - 'entity.displayName': 'X-23', - 'entity.id': '221', - }, - { - 'entity.lastSeenTimestamp': '2023-05-01T16:23:41.343Z', - 'entity.type': 'host', - 'entity.displayName': 'Prodigy', - 'entity.id': '222', - }, - { - 'entity.lastSeenTimestamp': '2023-02-03T07:17:47.909Z', - 'entity.type': 'container', - 'entity.displayName': 'Blindfold', - 'entity.id': '223', - }, - { - 'entity.lastSeenTimestamp': '2023-06-15T00:56:00.094Z', - 'entity.type': 'service', - 'entity.displayName': 'Ink', - 'entity.id': '224', - }, - { - 'entity.lastSeenTimestamp': '2024-04-28T22:32:11.149Z', - 'entity.type': 'container', - 'entity.displayName': 'Goldballs', - 'entity.id': '225', - }, - { - 'entity.lastSeenTimestamp': '2023-10-14T04:34:56.973Z', - 'entity.type': 'service', - 'entity.displayName': 'Magneto', - 'entity.id': '226', - }, - { - 'entity.lastSeenTimestamp': '2024-08-20T08:01:12.156Z', - 'entity.type': 'host', - 'entity.displayName': 'Juggernaut', - 'entity.id': '227', - }, - { - 'entity.lastSeenTimestamp': '2023-12-31T15:27:41.198Z', - 'entity.type': 'host', - 'entity.displayName': 'Mystique', - 'entity.id': '228', - }, - { - 'entity.lastSeenTimestamp': '2024-03-06T04:31:14.001Z', - 'entity.type': 'service', - 'entity.displayName': 'Sabretooth', - 'entity.id': '229', - }, - { - 'entity.lastSeenTimestamp': '2024-03-26T05:07:12.552Z', - 'entity.type': 'host', - 'entity.displayName': 'Toad', - 'entity.id': '230', - }, - { - 'entity.lastSeenTimestamp': '2024-05-20T17:34:56.098Z', - 'entity.type': 'service', - 'entity.displayName': 'Pyro', - 'entity.id': '231', - }, - { - 'entity.lastSeenTimestamp': '2023-04-12T16:53:27.530Z', - 'entity.type': 'host', - 'entity.displayName': 'Avalanche', - 'entity.id': '232', - }, - { - 'entity.lastSeenTimestamp': '2023-02-21T16:26:36.731Z', - 'entity.type': 'container', - 'entity.displayName': 'Blob', - 'entity.id': '233', - }, - { - 'entity.lastSeenTimestamp': '2023-03-23T03:52:18.017Z', - 'entity.type': 'host', - 'entity.displayName': 'Sauron', - 'entity.id': '234', - }, - { - 'entity.lastSeenTimestamp': '2024-04-10T21:31:37.929Z', - 'entity.type': 'container', - 'entity.displayName': 'Omega Red', - 'entity.id': '235', - }, - { - 'entity.lastSeenTimestamp': '2024-02-28T14:35:09.897Z', - 'entity.type': 'service', - 'entity.displayName': 'Mr. Sinister', - 'entity.id': '236', - }, - { - 'entity.lastSeenTimestamp': '2024-01-24T09:05:06.205Z', - 'entity.type': 'host', - 'entity.displayName': 'Apocalypse', - 'entity.id': '237', - }, - { - 'entity.lastSeenTimestamp': '2024-02-05T23:30:26.586Z', - 'entity.type': 'service', - 'entity.displayName': 'Genesis', - 'entity.id': '238', - }, - { - 'entity.lastSeenTimestamp': '2023-04-18T11:46:41.466Z', - 'entity.type': 'service', - 'entity.displayName': 'Archangel', - 'entity.id': '239', - }, - { - 'entity.lastSeenTimestamp': '2023-02-28T02:10:52.053Z', - 'entity.type': 'host', - 'entity.displayName': 'Holocaust', - 'entity.id': '240', - }, - { - 'entity.lastSeenTimestamp': '2024-04-03T12:19:13.947Z', - 'entity.type': 'host', - 'entity.displayName': 'Onslaught', - 'entity.id': '241', - }, - { - 'entity.lastSeenTimestamp': '2023-01-20T01:37:07.489Z', - 'entity.type': 'host', - 'entity.displayName': 'Exodus', - 'entity.id': '242', - }, - { - 'entity.lastSeenTimestamp': '2023-10-09T07:32:39.074Z', - 'entity.type': 'container', - 'entity.displayName': 'Gambit', - 'entity.id': '243', - }, - { - 'entity.lastSeenTimestamp': '2024-07-21T15:09:00.494Z', - 'entity.type': 'host', - 'entity.displayName': 'Rogue', - 'entity.id': '244', - }, - { - 'entity.lastSeenTimestamp': '2024-08-03T14:58:05.875Z', - 'entity.type': 'service', - 'entity.displayName': 'Magneto', - 'entity.id': '245', - }, - { - 'entity.lastSeenTimestamp': '2024-02-08T04:32:33.334Z', - 'entity.type': 'container', - 'entity.displayName': 'Longshot', - 'entity.id': '246', - }, - { - 'entity.lastSeenTimestamp': '2023-03-18T10:37:49.383Z', - 'entity.type': 'service', - 'entity.displayName': 'Dazzler', - 'entity.id': '247', - }, - { - 'entity.lastSeenTimestamp': '2024-07-11T17:35:31.669Z', - 'entity.type': 'service', - 'entity.displayName': 'Forge', - 'entity.id': '248', - }, - { - 'entity.lastSeenTimestamp': '2024-08-23T15:01:17.593Z', - 'entity.type': 'host', - 'entity.displayName': 'Mojo', - 'entity.id': '249', - }, - { - 'entity.lastSeenTimestamp': '2023-06-27T15:34:23.105Z', - 'entity.type': 'service', - 'entity.displayName': 'Spiral', - 'entity.id': '250', - }, - { - 'entity.lastSeenTimestamp': '2024-03-19T08:06:40.658Z', - 'entity.type': 'container', - 'entity.displayName': 'Warlock', - 'entity.id': '251', - }, - { - 'entity.lastSeenTimestamp': '2023-08-19T12:10:02.477Z', - 'entity.type': 'container', - 'entity.displayName': 'Magus', - 'entity.id': '252', - }, - { - 'entity.lastSeenTimestamp': '2024-05-30T12:20:06.653Z', - 'entity.type': 'service', - 'entity.displayName': 'Douglock', - 'entity.id': '253', - }, - { - 'entity.lastSeenTimestamp': '2023-05-31T17:48:07.719Z', - 'entity.type': 'service', - 'entity.displayName': 'Shatterstar', - 'entity.id': '254', - }, - { - 'entity.lastSeenTimestamp': '2023-12-10T05:35:40.666Z', - 'entity.type': 'service', - 'entity.displayName': 'Rictor', - 'entity.id': '255', - }, - { - 'entity.lastSeenTimestamp': '2024-02-02T22:18:47.168Z', - 'entity.type': 'host', - 'entity.displayName': 'Domino', - 'entity.id': '256', - }, - { - 'entity.lastSeenTimestamp': '2024-01-07T22:07:45.968Z', - 'entity.type': 'container', - 'entity.displayName': 'Cable', - 'entity.id': '257', - }, - { - 'entity.lastSeenTimestamp': '2023-01-15T11:22:54.155Z', - 'entity.type': 'host', - 'entity.displayName': 'Hope Summers', - 'entity.id': '258', - }, - { - 'entity.lastSeenTimestamp': '2023-03-26T13:56:10.553Z', - 'entity.type': 'service', - 'entity.displayName': 'Deadpool', - 'entity.id': '259', - }, - { - 'entity.lastSeenTimestamp': '2023-08-15T19:17:34.583Z', - 'entity.type': 'service', - 'entity.displayName': 'X-23', - 'entity.id': '260', - }, - { - 'entity.lastSeenTimestamp': '2024-06-26T09:02:40.512Z', - 'entity.type': 'host', - 'entity.displayName': 'Daken', - 'entity.id': '261', - }, - { - 'entity.lastSeenTimestamp': '2024-07-07T09:01:04.091Z', - 'entity.type': 'host', - 'entity.displayName': 'Wolverine', - 'entity.id': '262', - }, - { - 'entity.lastSeenTimestamp': '2023-10-15T22:25:29.643Z', - 'entity.type': 'service', - 'entity.displayName': 'Old Man Logan', - 'entity.id': '263', - }, - { - 'entity.lastSeenTimestamp': '2024-07-07T04:51:19.761Z', - 'entity.type': 'container', - 'entity.displayName': 'The Maker', - 'entity.id': '264', - }, - { - 'entity.lastSeenTimestamp': '2024-03-13T14:00:51.289Z', - 'entity.type': 'container', - 'entity.displayName': 'Ultimate Thor', - 'entity.id': '265', - }, - { - 'entity.lastSeenTimestamp': '2023-07-23T10:13:07.651Z', - 'entity.type': 'service', - 'entity.displayName': 'Ultimate Iron Man', - 'entity.id': '266', - }, - { - 'entity.lastSeenTimestamp': '2023-08-20T07:09:20.148Z', - 'entity.type': 'container', - 'entity.displayName': 'Ultimate Hulk', - 'entity.id': '267', - }, - { - 'entity.lastSeenTimestamp': '2024-09-08T10:53:13.256Z', - 'entity.type': 'service', - 'entity.displayName': 'Ultimate Captain America', - 'entity.id': '268', - }, - { - 'entity.lastSeenTimestamp': '2024-09-15T03:57:28.175Z', - 'entity.type': 'container', - 'entity.displayName': - 'Sed dignissim libero a diam sagittis, in convallis leo pellentesque. Cras ut sapien sed lacus scelerisque vehicula. Pellentesque at purus pulvinar, mollis justo hendrerit, pharetra purus. Morbi dapibus, augue et volutpat ultricies, neque quam sollicitudin mauris, vitae luctus ex libero id erat. Suspendisse risus lectus, scelerisque vel odio sed.', - 'entity.id': '269', - alertsCount: 4, - }, - { - 'entity.lastSeenTimestamp': '2023-10-22T13:49:53.092Z', - 'entity.type': 'host', - 'entity.displayName': 'Silk', - 'entity.id': '270', - }, - { - 'entity.lastSeenTimestamp': '2023-01-13T00:36:25.773Z', - 'entity.type': 'host', - 'entity.displayName': 'Scarlet Spider', - 'entity.id': '271', - }, - { - 'entity.lastSeenTimestamp': '2023-12-10T19:31:42.994Z', - 'entity.type': 'service', - 'entity.displayName': 'Ben Reilly', - 'entity.id': '272', - }, - { - 'entity.lastSeenTimestamp': '2023-01-17T09:49:30.447Z', - 'entity.type': 'service', - 'entity.displayName': 'Miles Morales', - 'entity.id': '273', - }, - { - 'entity.lastSeenTimestamp': '2023-01-02T18:45:44.012Z', - 'entity.type': 'container', - 'entity.displayName': 'Spider-Ham', - 'entity.id': '274', - }, - { - 'entity.lastSeenTimestamp': '2023-06-28T22:50:08.414Z', - 'entity.type': 'container', - 'entity.displayName': 'Agent Venom', - 'entity.id': '275', - }, - { - 'entity.lastSeenTimestamp': '2023-03-30T17:01:35.995Z', - 'entity.type': 'service', - 'entity.displayName': 'Anti-Venom', - 'entity.id': '276', - }, - { - 'entity.lastSeenTimestamp': '2023-06-11T05:23:11.367Z', - 'entity.type': 'host', - 'entity.displayName': 'Toxin', - 'entity.id': '277', - }, - { - 'entity.lastSeenTimestamp': '2023-07-22T15:27:17.077Z', - 'entity.type': 'service', - 'entity.displayName': 'Morbius', - 'entity.id': '278', - }, - { - 'entity.lastSeenTimestamp': '2024-01-26T11:19:34.147Z', - 'entity.type': 'host', - 'entity.displayName': 'Kraven the Hunter', - 'entity.id': '279', - }, - { - 'entity.lastSeenTimestamp': '2024-06-18T09:03:01.111Z', - 'entity.type': 'container', - 'entity.displayName': 'Doctor Octopus', - 'entity.id': '280', - }, - { - 'entity.lastSeenTimestamp': '2024-07-27T14:08:12.583Z', - 'entity.type': 'container', - 'entity.displayName': 'Green Goblin', - 'entity.id': '281', - }, - { - 'entity.lastSeenTimestamp': '2023-01-12T01:38:45.243Z', - 'entity.type': 'host', - 'entity.displayName': 'Electro', - 'entity.id': '282', - }, - { - 'entity.lastSeenTimestamp': '2024-04-19T05:33:59.289Z', - 'entity.type': 'container', - 'entity.displayName': 'Rhino', - 'entity.id': '283', - }, - { - 'entity.lastSeenTimestamp': '2023-04-13T22:06:02.389Z', - 'entity.type': 'service', - 'entity.displayName': 'Shocker', - 'entity.id': '284', - }, - { - 'entity.lastSeenTimestamp': '2023-01-26T15:36:08.782Z', - 'entity.type': 'host', - 'entity.displayName': 'Vulture', - 'entity.id': '285', - }, - { - 'entity.lastSeenTimestamp': '2023-11-11T19:54:14.523Z', - 'entity.type': 'container', - 'entity.displayName': 'Sandman', - 'entity.id': '286', - }, - { - 'entity.lastSeenTimestamp': '2023-12-06T06:20:06.995Z', - 'entity.type': 'host', - 'entity.displayName': 'Mysterio', - 'entity.id': '287', - }, - { - 'entity.lastSeenTimestamp': '2023-07-23T04:30:35.686Z', - 'entity.type': 'service', - 'entity.displayName': 'Black Cat', - 'entity.id': '288', - }, - { - 'entity.lastSeenTimestamp': '2023-01-18T03:09:26.047Z', - 'entity.type': 'host', - 'entity.displayName': 'Silver Sable', - 'entity.id': '289', - }, - { - 'entity.lastSeenTimestamp': '2024-06-08T12:42:08.485Z', - 'entity.type': 'service', - 'entity.displayName': 'Chameleon', - 'entity.id': '290', - }, - { - 'entity.lastSeenTimestamp': '2023-08-18T03:34:28.230Z', - 'entity.type': 'container', - 'entity.displayName': 'Hammerhead', - 'entity.id': '291', - }, - { - 'entity.lastSeenTimestamp': '2024-04-13T01:42:03.890Z', - 'entity.type': 'container', - 'entity.displayName': 'Tombstone', - 'entity.id': '292', - }, - { - 'entity.lastSeenTimestamp': '2023-11-21T17:39:56.066Z', - 'entity.type': 'container', - 'entity.displayName': 'Alistair Smythe', - 'entity.id': '293', - }, - { - 'entity.lastSeenTimestamp': '2024-02-29T04:45:41.113Z', - 'entity.type': 'host', - 'entity.displayName': 'The Beetle', - 'entity.id': '294', - }, - { - 'entity.lastSeenTimestamp': '2024-08-12T07:40:35.827Z', - 'entity.type': 'host', - 'entity.displayName': 'The Prowler', - 'entity.id': '295', - }, - { - 'entity.lastSeenTimestamp': '2023-11-27T23:09:49.629Z', - 'entity.type': 'service', - 'entity.displayName': 'Scorpion', - 'entity.id': '296', - }, - { - 'entity.lastSeenTimestamp': '2024-08-29T21:24:37.304Z', - 'entity.type': 'container', - 'entity.displayName': 'Jackal', - 'entity.id': '297', - }, - { - 'entity.lastSeenTimestamp': '2023-03-25T03:08:42.970Z', - 'entity.type': 'container', - 'entity.displayName': 'Morlun', - 'entity.id': '298', - }, - { - 'entity.lastSeenTimestamp': '2023-12-12T01:01:52.801Z', - 'entity.type': 'container', - 'entity.displayName': 'Lizard', - 'entity.id': '299', - }, - { - 'entity.lastSeenTimestamp': '2024-02-22T02:29:11.333Z', - 'entity.type': 'service', - 'entity.displayName': 'Kingpin', - 'entity.id': '300', - }, - { - 'entity.lastSeenTimestamp': '2024-09-03T19:31:38.700Z', - 'entity.type': 'host', - 'entity.displayName': 'Carnage', - 'entity.id': '301', - }, - { - 'entity.lastSeenTimestamp': '2023-04-09T17:55:20.565Z', - 'entity.type': 'container', - 'entity.displayName': 'Norman Osborn', - 'entity.id': '302', - }, - { - 'entity.lastSeenTimestamp': '2023-11-15T11:23:39.657Z', - 'entity.type': 'container', - 'entity.displayName': 'Harry Osborn', - 'entity.id': '303', - }, - { - 'entity.lastSeenTimestamp': '2024-08-16T08:14:11.415Z', - 'entity.type': 'service', - 'entity.displayName': 'Hobgoblin', - 'entity.id': '304', - }, - { - 'entity.lastSeenTimestamp': '2023-04-09T06:48:50.111Z', - 'entity.type': 'container', - 'entity.displayName': 'Phil Urich', - 'entity.id': '305', - }, - { - 'entity.lastSeenTimestamp': '2023-10-07T15:00:25.174Z', - 'entity.type': 'host', - 'entity.displayName': 'Demogoblin', - 'entity.id': '306', - }, - { - 'entity.lastSeenTimestamp': '2024-05-04T22:13:00.266Z', - 'entity.type': 'container', - 'entity.displayName': 'Red Goblin', - 'entity.id': '307', - }, - { - 'entity.lastSeenTimestamp': '2024-04-04T23:46:04.650Z', - 'entity.type': 'container', - 'entity.displayName': 'Doctor Octopus', - 'entity.id': '308', - }, - { - 'entity.lastSeenTimestamp': '2023-03-09T03:17:41.028Z', - 'entity.type': 'container', - 'entity.displayName': 'Otto Octavius', - 'entity.id': '309', - }, - { - 'entity.lastSeenTimestamp': '2023-02-15T01:52:08.165Z', - 'entity.type': 'service', - 'entity.displayName': 'Spider-Slayer', - 'entity.id': '310', - }, - { - 'entity.lastSeenTimestamp': '2024-05-18T16:03:17.334Z', - 'entity.type': 'container', - 'entity.displayName': 'The Spot', - 'entity.id': '311', - }, - { - 'entity.lastSeenTimestamp': '2023-10-24T01:14:40.519Z', - 'entity.type': 'host', - 'entity.displayName': 'White Tiger', - 'entity.id': '312', - }, - { - 'entity.lastSeenTimestamp': '2023-11-25T03:29:54.122Z', - 'entity.type': 'container', - 'entity.displayName': 'Kang', - 'entity.id': '313', - }, - { - 'entity.lastSeenTimestamp': '2023-03-10T14:39:44.761Z', - 'entity.type': 'container', - 'entity.displayName': 'Baron Zemo', - 'entity.id': '314', - }, - { - 'entity.lastSeenTimestamp': '2023-05-02T09:25:50.743Z', - 'entity.type': 'host', - 'entity.displayName': 'Red Skull', - 'entity.id': '315', - }, - { - 'entity.lastSeenTimestamp': '2023-04-09T14:57:15.653Z', - 'entity.type': 'container', - 'entity.displayName': 'MODOK', - 'entity.id': '316', - }, - { - 'entity.lastSeenTimestamp': '2023-12-02T10:21:33.045Z', - 'entity.type': 'service', - 'entity.displayName': 'Taskmaster', - 'entity.id': '317', - }, - { - 'entity.lastSeenTimestamp': '2023-09-26T12:18:47.857Z', - 'entity.type': 'service', - 'entity.displayName': 'Ultron', - 'entity.id': '318', - }, - { - 'entity.lastSeenTimestamp': '2023-06-29T22:13:32.744Z', - 'entity.type': 'container', - 'entity.displayName': 'Crossbones', - 'entity.id': '319', - }, - { - 'entity.lastSeenTimestamp': '2023-04-29T16:04:40.552Z', - 'entity.type': 'service', - 'entity.displayName': 'Madame Hydra', - 'entity.id': '320', - }, - { - 'entity.lastSeenTimestamp': '2023-07-26T05:34:55.857Z', - 'entity.type': 'host', - 'entity.displayName': 'The Leader', - 'entity.id': '321', - }, - { - 'entity.lastSeenTimestamp': '2023-05-23T13:21:34.771Z', - 'entity.type': 'service', - 'entity.displayName': 'Abomination', - 'entity.id': '322', - }, - { - 'entity.lastSeenTimestamp': '2024-05-06T22:15:26.389Z', - 'entity.type': 'container', - 'entity.displayName': 'The Mandarin', - 'entity.id': '323', - }, - { - 'entity.lastSeenTimestamp': '2024-01-08T09:12:59.615Z', - 'entity.type': 'service', - 'entity.displayName': 'Fin Fang Foom', - 'entity.id': '324', - }, - { - 'entity.lastSeenTimestamp': '2023-07-07T15:39:12.867Z', - 'entity.type': 'container', - 'entity.displayName': 'Killmonger', - 'entity.id': '325', - }, - { - 'entity.lastSeenTimestamp': '2023-12-04T02:42:55.907Z', - 'entity.type': 'container', - 'entity.displayName': 'Ulysses Klaue', - 'entity.id': '326', - }, - { - 'entity.lastSeenTimestamp': '2024-01-01T10:14:42.258Z', - 'entity.type': 'container', - 'entity.displayName': 'The Collector', - 'entity.id': '327', - }, - { - 'entity.lastSeenTimestamp': '2024-07-21T02:20:14.626Z', - 'entity.type': 'container', - 'entity.displayName': 'The Grandmaster', - 'entity.id': '328', - }, - { - 'entity.lastSeenTimestamp': '2024-04-19T01:54:14.317Z', - 'entity.type': 'service', - 'entity.displayName': 'Thanos', - 'entity.id': '329', - }, - { - 'entity.lastSeenTimestamp': '2023-12-15T04:43:05.141Z', - 'entity.type': 'host', - 'entity.displayName': 'Darkseid', - 'entity.id': '330', - }, - { - 'entity.lastSeenTimestamp': '2023-06-20T14:32:29.968Z', - 'entity.type': 'service', - 'entity.displayName': 'Lex Luthor', - 'entity.id': '331', - }, - { - 'entity.lastSeenTimestamp': '2023-11-02T15:33:40.790Z', - 'entity.type': 'container', - 'entity.displayName': 'Bane', - 'entity.id': '332', - }, - { - 'entity.lastSeenTimestamp': '2024-06-09T08:34:20.039Z', - 'entity.type': 'host', - 'entity.displayName': 'Brainiac', - 'entity.id': '333', - }, - { - 'entity.lastSeenTimestamp': '2024-08-30T14:00:25.077Z', - 'entity.type': 'container', - 'entity.displayName': 'Doomsday', - 'entity.id': '334', - }, - { - 'entity.lastSeenTimestamp': '2024-02-26T18:03:06.283Z', - 'entity.type': 'service', - 'entity.displayName': 'General Zod', - 'entity.id': '335', - }, - { - 'entity.lastSeenTimestamp': '2023-10-30T05:16:19.508Z', - 'entity.type': 'host', - 'entity.displayName': "Ra's al Ghul", - 'entity.id': '336', - }, - { - 'entity.lastSeenTimestamp': '2023-04-05T20:09:22.332Z', - 'entity.type': 'host', - 'entity.displayName': 'Scarecrow', - 'entity.id': '337', - }, - { - 'entity.lastSeenTimestamp': '2023-06-09T06:46:09.887Z', - 'entity.type': 'service', - 'entity.displayName': 'The Joker', - 'entity.id': '338', - }, - { - 'entity.lastSeenTimestamp': '2023-04-26T15:02:13.202Z', - 'entity.type': 'host', - 'entity.displayName': 'Harley Quinn', - 'entity.id': '339', - }, - { - 'entity.lastSeenTimestamp': '2024-04-09T05:21:09.975Z', - 'entity.type': 'service', - 'entity.displayName': 'Poison Ivy', - 'entity.id': '340', - }, - { - 'entity.lastSeenTimestamp': '2023-06-05T04:53:00.171Z', - 'entity.type': 'service', - 'entity.displayName': 'The Riddler', - 'entity.id': '341', - }, - { - 'entity.lastSeenTimestamp': '2024-03-07T01:23:08.698Z', - 'entity.type': 'host', - 'entity.displayName': 'Penguin', - 'entity.id': '342', - }, - { - 'entity.lastSeenTimestamp': '2024-05-17T13:08:12.434Z', - 'entity.type': 'container', - 'entity.displayName': 'Two-Face', - 'entity.id': '343', - }, - { - 'entity.lastSeenTimestamp': '2024-03-13T16:39:26.987Z', - 'entity.type': 'service', - 'entity.displayName': 'Mr. Freeze', - 'entity.id': '344', - }, - { - 'entity.lastSeenTimestamp': '2024-01-01T06:31:32.470Z', - 'entity.type': 'host', - 'entity.displayName': 'Clayface', - 'entity.id': '345', - }, - { - 'entity.lastSeenTimestamp': '2024-06-24T16:27:01.156Z', - 'entity.type': 'service', - 'entity.displayName': 'Hush', - 'entity.id': '346', - }, - { - 'entity.lastSeenTimestamp': '2023-10-19T14:35:47.544Z', - 'entity.type': 'host', - 'entity.displayName': 'Black Mask', - 'entity.id': '347', - }, - { - 'entity.lastSeenTimestamp': '2023-10-24T13:57:07.539Z', - 'entity.type': 'host', - 'entity.displayName': 'Killer Croc', - 'entity.id': '348', - }, - { - 'entity.lastSeenTimestamp': '2023-02-19T09:40:44.538Z', - 'entity.type': 'service', - 'entity.displayName': 'Deathstroke', - 'entity.id': '349', - }, - { - 'entity.lastSeenTimestamp': '2023-03-25T19:22:45.889Z', - 'entity.type': 'service', - 'entity.displayName': 'Deadshot', - 'entity.id': '350', - }, - { - 'entity.lastSeenTimestamp': '2024-06-08T03:10:02.475Z', - 'entity.type': 'container', - 'entity.displayName': 'Amanda Waller', - 'entity.id': '351', - }, - { - 'entity.lastSeenTimestamp': '2023-01-04T03:49:07.210Z', - 'entity.type': 'host', - 'entity.displayName': 'Captain Boomerang', - 'entity.id': '352', - }, - { - 'entity.lastSeenTimestamp': '2023-04-10T20:53:14.367Z', - 'entity.type': 'host', - 'entity.displayName': 'Katana', - 'entity.id': '353', - }, - { - 'entity.lastSeenTimestamp': '2024-04-25T09:42:55.170Z', - 'entity.type': 'host', - 'entity.displayName': 'El Diablo', - 'entity.id': '354', - }, - { - 'entity.lastSeenTimestamp': '2024-05-10T00:44:03.472Z', - 'entity.type': 'host', - 'entity.displayName': 'Enchantress', - 'entity.id': '355', - }, - { - 'entity.lastSeenTimestamp': '2024-02-16T03:47:56.021Z', - 'entity.type': 'service', - 'entity.displayName': 'Rick Flag', - 'entity.id': '356', - }, - { - 'entity.lastSeenTimestamp': '2023-09-30T16:45:27.670Z', - 'entity.type': 'host', - 'entity.displayName': 'King Shark', - 'entity.id': '357', - }, - { - 'entity.lastSeenTimestamp': '2023-10-14T03:04:21.380Z', - 'entity.type': 'host', - 'entity.displayName': 'Peacemaker', - 'entity.id': '358', - }, - { - 'entity.lastSeenTimestamp': '2023-06-27T20:42:18.732Z', - 'entity.type': 'host', - 'entity.displayName': 'Bloodsport', - 'entity.id': '359', - }, - { - 'entity.lastSeenTimestamp': '2024-05-25T22:56:14.675Z', - 'entity.type': 'container', - 'entity.displayName': 'Weasel', - 'entity.id': '360', - }, - { - 'entity.lastSeenTimestamp': '2024-05-15T05:34:39.704Z', - 'entity.type': 'container', - 'entity.displayName': 'Javelin', - 'entity.id': '361', - }, - { - 'entity.lastSeenTimestamp': '2024-07-18T13:40:24.040Z', - 'entity.type': 'container', - 'entity.displayName': 'Ratcatcher', - 'entity.id': '362', - }, - { - 'entity.lastSeenTimestamp': '2023-08-31T03:02:00.545Z', - 'entity.type': 'container', - 'entity.displayName': 'T.D.K.', - 'entity.id': '363', - }, - { - 'entity.lastSeenTimestamp': '2024-08-27T11:13:19.374Z', - 'entity.type': 'container', - 'entity.displayName': 'Doctor Fate', - 'entity.id': '364', - }, - { - 'entity.lastSeenTimestamp': '2023-08-29T06:47:41.545Z', - 'entity.type': 'container', - 'entity.displayName': 'Hawkman', - 'entity.id': '365', - }, - { - 'entity.lastSeenTimestamp': '2024-04-30T00:01:35.041Z', - 'entity.type': 'service', - 'entity.displayName': 'Hawkgirl', - 'entity.id': '366', - }, - { - 'entity.lastSeenTimestamp': '2024-01-24T01:02:59.317Z', - 'entity.type': 'container', - 'entity.displayName': 'Black Adam', - 'entity.id': '367', - }, - { - 'entity.lastSeenTimestamp': '2023-11-08T14:30:16.054Z', - 'entity.type': 'service', - 'entity.displayName': 'Atom Smasher', - 'entity.id': '368', - }, - { - 'entity.lastSeenTimestamp': '2024-08-02T05:40:07.271Z', - 'entity.type': 'host', - 'entity.displayName': 'Cyclone', - 'entity.id': '369', - }, - { - 'entity.lastSeenTimestamp': '2024-03-24T19:11:13.807Z', - 'entity.type': 'host', - 'entity.displayName': 'Stargirl', - 'entity.id': '370', - }, - { - 'entity.lastSeenTimestamp': '2024-01-25T19:31:31.536Z', - 'entity.type': 'host', - 'entity.displayName': 'Hourman', - 'entity.id': '371', - }, - { - 'entity.lastSeenTimestamp': '2024-05-20T22:09:46.339Z', - 'entity.type': 'service', - 'entity.displayName': 'Wildcat', - 'entity.id': '372', - }, - { - 'entity.lastSeenTimestamp': '2023-07-31T01:51:08.575Z', - 'entity.type': 'host', - 'entity.displayName': 'Green Arrow', - 'entity.id': '373', - }, - { - 'entity.lastSeenTimestamp': '2024-03-23T22:01:53.447Z', - 'entity.type': 'container', - 'entity.displayName': 'Speedy', - 'entity.id': '374', - }, - { - 'entity.lastSeenTimestamp': '2024-02-11T22:26:31.584Z', - 'entity.type': 'service', - 'entity.displayName': 'Arsenal', - 'entity.id': '375', - }, - { - 'entity.lastSeenTimestamp': '2024-04-06T12:30:22.601Z', - 'entity.type': 'service', - 'entity.displayName': 'Red Hood', - 'entity.id': '376', - }, - { - 'entity.lastSeenTimestamp': '2023-09-13T07:02:26.095Z', - 'entity.type': 'service', - 'entity.displayName': 'Batgirl', - 'entity.id': '377', - }, - { - 'entity.lastSeenTimestamp': '2024-07-07T22:22:48.331Z', - 'entity.type': 'container', - 'entity.displayName': 'Oracle', - 'entity.id': '378', - }, - { - 'entity.lastSeenTimestamp': '2024-08-09T21:51:59.774Z', - 'entity.type': 'host', - 'entity.displayName': 'Huntress', - 'entity.id': '379', - }, - { - 'entity.lastSeenTimestamp': '2024-02-04T21:15:45.848Z', - 'entity.type': 'service', - 'entity.displayName': 'Cassandra Cain', - 'entity.id': '380', - }, - { - 'entity.lastSeenTimestamp': '2023-07-23T14:22:33.033Z', - 'entity.type': 'host', - 'entity.displayName': 'Azrael', - 'entity.id': '381', - }, - { - 'entity.lastSeenTimestamp': '2024-09-04T05:28:23.197Z', - 'entity.type': 'container', - 'entity.displayName': 'Batwoman', - 'entity.id': '382', - }, - { - 'entity.lastSeenTimestamp': '2023-06-27T08:09:37.626Z', - 'entity.type': 'container', - 'entity.displayName': 'Stephanie Brown', - 'entity.id': '383', - }, - { - 'entity.lastSeenTimestamp': '2023-12-20T08:14:23.553Z', - 'entity.type': 'host', - 'entity.displayName': 'The Question', - 'entity.id': '384', - }, - { - 'entity.lastSeenTimestamp': '2024-03-17T00:19:48.826Z', - 'entity.type': 'container', - 'entity.displayName': 'Blue Beetle', - 'entity.id': '385', - }, - { - 'entity.lastSeenTimestamp': '2024-02-17T20:55:20.634Z', - 'entity.type': 'container', - 'entity.displayName': 'Booster Gold', - 'entity.id': '386', - }, - { - 'entity.lastSeenTimestamp': '2023-02-14T10:24:49.445Z', - 'entity.type': 'host', - 'entity.displayName': 'Plastic Man', - 'entity.id': '387', - }, - { - 'entity.lastSeenTimestamp': '2024-05-10T06:49:45.226Z', - 'entity.type': 'container', - 'entity.displayName': 'Metamorpho', - 'entity.id': '388', - }, - { - 'entity.lastSeenTimestamp': '2023-08-28T11:04:03.884Z', - 'entity.type': 'host', - 'entity.displayName': 'The Spectre', - 'entity.id': '389', - }, - { - 'entity.lastSeenTimestamp': '2023-06-03T09:16:22.294Z', - 'entity.type': 'service', - 'entity.displayName': 'Etrigan', - 'entity.id': '390', - }, - { - 'entity.lastSeenTimestamp': '2023-05-27T15:43:31.368Z', - 'entity.type': 'host', - 'entity.displayName': 'Swamp Thing', - 'entity.id': '391', - }, - { - 'entity.lastSeenTimestamp': '2024-01-23T00:27:36.339Z', - 'entity.type': 'service', - 'entity.displayName': 'Constantine', - 'entity.id': '392', - }, - { - 'entity.lastSeenTimestamp': '2023-12-19T09:00:36.251Z', - 'entity.type': 'host', - 'entity.displayName': 'Zatanna', - 'entity.id': '393', - }, - { - 'entity.lastSeenTimestamp': '2024-02-11T09:31:14.413Z', - 'entity.type': 'host', - 'entity.displayName': 'Doctor Fate', - 'entity.id': '394', - }, - { - 'entity.lastSeenTimestamp': '2024-08-15T14:04:15.345Z', - 'entity.type': 'service', - 'entity.displayName': 'Martian Manhunter', - 'entity.id': '395', - }, - { - 'entity.lastSeenTimestamp': '2024-03-23T06:41:28.527Z', - 'entity.type': 'container', - 'entity.displayName': 'Firestorm', - 'entity.id': '396', - }, - { - 'entity.lastSeenTimestamp': '2023-03-29T19:22:53.314Z', - 'entity.type': 'service', - 'entity.displayName': 'Captain Atom', - 'entity.id': '397', - }, - { - 'entity.lastSeenTimestamp': '2024-05-03T02:22:19.643Z', - 'entity.type': 'service', - 'entity.displayName': 'The Atom', - 'entity.id': '398', - }, - { - 'entity.lastSeenTimestamp': '2024-05-12T05:55:36.153Z', - 'entity.type': 'service', - 'entity.displayName': 'Vixen', - 'entity.id': '399', - }, - { - 'entity.lastSeenTimestamp': '2023-03-01T07:39:44.249Z', - 'entity.type': 'service', - 'entity.displayName': 'Animal Man', - 'entity.id': '400', - }, - { - 'entity.lastSeenTimestamp': '2023-05-20T14:24:33.191Z', - 'entity.type': 'host', - 'entity.displayName': 'Hawk', - 'entity.id': '401', - }, - { - 'entity.lastSeenTimestamp': '2023-06-24T16:44:21.444Z', - 'entity.type': 'host', - 'entity.displayName': 'Dove', - 'entity.id': '402', - }, - { - 'entity.lastSeenTimestamp': '2024-04-05T00:50:29.260Z', - 'entity.type': 'host', - 'entity.displayName': 'Steel', - 'entity.id': '403', - }, - { - 'entity.lastSeenTimestamp': '2024-05-01T07:44:47.694Z', - 'entity.type': 'host', - 'entity.displayName': 'Guardian', - 'entity.id': '404', - }, - { - 'entity.lastSeenTimestamp': '2024-08-10T20:46:37.204Z', - 'entity.type': 'container', - 'entity.displayName': 'The Phantom Stranger', - 'entity.id': '405', - }, - { - 'entity.lastSeenTimestamp': '2024-04-06T11:04:12.556Z', - 'entity.type': 'service', - 'entity.displayName': 'Lobo', - 'entity.id': '406', - }, - { - 'entity.lastSeenTimestamp': '2023-11-24T01:39:36.878Z', - 'entity.type': 'host', - 'entity.displayName': 'Red Tornado', - 'entity.id': '407', - }, - { - 'entity.lastSeenTimestamp': '2024-08-05T14:00:37.985Z', - 'entity.type': 'service', - 'entity.displayName': 'Miss Martian', - 'entity.id': '408', - }, - { - 'entity.lastSeenTimestamp': '2024-01-23T18:57:18.692Z', - 'entity.type': 'container', - 'entity.displayName': 'Bizarro', - 'entity.id': '409', - }, - { - 'entity.lastSeenTimestamp': '2023-01-29T08:35:22.194Z', - 'entity.type': 'service', - 'entity.displayName': 'Black Lightning', - 'entity.id': '410', - }, - { - 'entity.lastSeenTimestamp': '2024-04-03T21:32:10.035Z', - 'entity.type': 'container', - 'entity.displayName': 'Katana', - 'entity.id': '411', - }, - { - 'entity.lastSeenTimestamp': '2024-02-05T09:18:03.386Z', - 'entity.type': 'service', - 'entity.displayName': 'Mr. Terrific', - 'entity.id': '412', - }, - { - 'entity.lastSeenTimestamp': '2024-05-09T01:04:11.713Z', - 'entity.type': 'host', - 'entity.displayName': 'Plastic Man', - 'entity.id': '413', - }, - { - 'entity.lastSeenTimestamp': '2023-03-25T15:26:53.790Z', - 'entity.type': 'host', - 'entity.displayName': 'Shazam', - 'entity.id': '414', - }, - { - 'entity.lastSeenTimestamp': '2023-07-11T11:07:31.377Z', - 'entity.type': 'service', - 'entity.displayName': 'Spawn', - 'entity.id': '415', - }, - { - 'entity.lastSeenTimestamp': '2023-09-08T10:01:26.864Z', - 'entity.type': 'host', - 'entity.displayName': 'Invincible', - 'entity.id': '416', - }, - { - 'entity.lastSeenTimestamp': '2024-07-14T15:51:35.763Z', - 'entity.type': 'container', - 'entity.displayName': 'Atom Eve', - 'entity.id': '417', - }, - { - 'entity.lastSeenTimestamp': '2024-06-26T21:44:30.555Z', - 'entity.type': 'container', - 'entity.displayName': 'Rex Splode', - 'entity.id': '418', - }, - { - 'entity.lastSeenTimestamp': '2023-07-05T04:20:35.073Z', - 'entity.type': 'container', - 'entity.displayName': 'Allen the Alien', - 'entity.id': '419', - }, - { - 'entity.lastSeenTimestamp': '2024-05-31T19:57:53.543Z', - 'entity.type': 'service', - 'entity.displayName': 'Omni-Man', - 'entity.id': '420', - }, - { - 'entity.lastSeenTimestamp': '2023-02-19T17:22:07.379Z', - 'entity.type': 'service', - 'entity.displayName': 'The Tick', - 'entity.id': '421', - }, - { - 'entity.lastSeenTimestamp': '2023-12-17T22:51:04.060Z', - 'entity.type': 'host', - 'entity.displayName': 'Arthur', - 'entity.id': '422', - }, - { - 'entity.lastSeenTimestamp': '2024-03-09T23:54:47.229Z', - 'entity.type': 'service', - 'entity.displayName': 'Big Daddy', - 'entity.id': '423', - }, - { - 'entity.lastSeenTimestamp': '2024-07-14T11:52:37.828Z', - 'entity.type': 'service', - 'entity.displayName': 'Hit-Girl', - 'entity.id': '424', - }, - { - 'entity.lastSeenTimestamp': '2023-02-08T21:15:09.242Z', - 'entity.type': 'container', - 'entity.displayName': 'Kick-Ass', - 'entity.id': '425', - }, - { - 'entity.lastSeenTimestamp': '2024-03-01T17:58:53.274Z', - 'entity.type': 'host', - 'entity.displayName': 'Hellboy', - 'entity.id': '426', - }, - { - 'entity.lastSeenTimestamp': '2023-11-04T20:37:28.218Z', - 'entity.type': 'host', - 'entity.displayName': 'Abe Sapien', - 'entity.id': '427', - }, - { - 'entity.lastSeenTimestamp': '2024-05-16T15:38:01.584Z', - 'entity.type': 'service', - 'entity.displayName': 'Liz Sherman', - 'entity.id': '428', - }, - { - 'entity.lastSeenTimestamp': '2023-03-28T13:40:51.501Z', - 'entity.type': 'container', - 'entity.displayName': 'The Mask', - 'entity.id': '429', - }, - { - 'entity.lastSeenTimestamp': '2023-07-22T10:39:48.045Z', - 'entity.type': 'service', - 'entity.displayName': 'Judge Dredd', - 'entity.id': '430', - }, - { - 'entity.lastSeenTimestamp': '2023-11-10T02:21:09.389Z', - 'entity.type': 'service', - 'entity.displayName': 'Tank Girl', - 'entity.id': '431', - }, - { - 'entity.lastSeenTimestamp': '2024-04-21T16:23:33.730Z', - 'entity.type': 'container', - 'entity.displayName': 'Shadowman', - 'entity.id': '432', - }, - { - 'entity.lastSeenTimestamp': '2023-08-17T19:31:07.282Z', - 'entity.type': 'container', - 'entity.displayName': 'Bloodshot', - 'entity.id': '433', - }, - { - 'entity.lastSeenTimestamp': '2023-04-23T10:05:19.825Z', - 'entity.type': 'service', - 'entity.displayName': 'X-O Manowar', - 'entity.id': '434', - }, - { - 'entity.lastSeenTimestamp': '2024-04-30T21:58:46.410Z', - 'entity.type': 'host', - 'entity.displayName': 'Harbinger', - 'entity.id': '435', - }, - { - 'entity.lastSeenTimestamp': '2023-07-14T05:26:30.493Z', - 'entity.type': 'service', - 'entity.displayName': 'Ninjak', - 'entity.id': '436', - }, - { - 'entity.lastSeenTimestamp': '2024-01-30T09:21:55.939Z', - 'entity.type': 'host', - 'entity.displayName': 'Faith', - 'entity.id': '437', - }, - { - 'entity.lastSeenTimestamp': '2024-02-17T20:36:23.898Z', - 'entity.type': 'host', - 'entity.displayName': 'Archer', - 'entity.id': '438', - }, - { - 'entity.lastSeenTimestamp': '2023-04-04T15:08:08.423Z', - 'entity.type': 'container', - 'entity.displayName': 'Armstrong', - 'entity.id': '439', - }, - { - 'entity.lastSeenTimestamp': '2024-07-29T11:54:01.693Z', - 'entity.type': 'host', - 'entity.displayName': 'Eternal Warrior', - 'entity.id': '440', - }, - { - 'entity.lastSeenTimestamp': '2023-11-02T09:56:15.646Z', - 'entity.type': 'host', - 'entity.displayName': 'Quantum', - 'entity.id': '441', - }, - { - 'entity.lastSeenTimestamp': '2023-04-06T02:07:23.857Z', - 'entity.type': 'container', - 'entity.displayName': 'Woody', - 'entity.id': '442', - }, - { - 'entity.lastSeenTimestamp': '2023-05-20T10:33:26.328Z', - 'entity.type': 'host', - 'entity.displayName': 'The Darkness', - 'entity.id': '443', - }, - { - 'entity.lastSeenTimestamp': '2023-12-03T23:59:21.627Z', - 'entity.type': 'container', - 'entity.displayName': 'Witchblade', - 'entity.id': '444', - }, - { - 'entity.lastSeenTimestamp': '2023-05-31T10:56:01.829Z', - 'entity.type': 'container', - 'entity.displayName': 'Ripclaw', - 'entity.id': '445', - }, - { - 'entity.lastSeenTimestamp': '2024-07-28T11:56:20.407Z', - 'entity.type': 'host', - 'entity.displayName': 'Warblade', - 'entity.id': '446', - }, - { - 'entity.lastSeenTimestamp': '2023-05-03T18:24:08.227Z', - 'entity.type': 'host', - 'entity.displayName': 'Savage Dragon', - 'entity.id': '447', - }, - { - 'entity.lastSeenTimestamp': '2024-07-15T09:05:19.621Z', - 'entity.type': 'host', - 'entity.displayName': 'Spawn', - 'entity.id': '448', - }, - { - 'entity.lastSeenTimestamp': '2024-04-16T13:06:48.941Z', - 'entity.type': 'host', - 'entity.displayName': 'Witchblade', - 'entity.id': '449', - }, - { - 'entity.lastSeenTimestamp': '2024-04-22T12:52:06.912Z', - 'entity.type': 'container', - 'entity.displayName': 'Invincible', - 'entity.id': '450', - }, - { - 'entity.lastSeenTimestamp': '2023-02-23T23:57:49.389Z', - 'entity.type': 'host', - 'entity.displayName': 'The Maxx', - 'entity.id': '451', - }, - { - 'entity.lastSeenTimestamp': '2024-04-17T01:12:16.359Z', - 'entity.type': 'service', - 'entity.displayName': 'Lady Death', - 'entity.id': '452', - }, - { - 'entity.lastSeenTimestamp': '2024-05-07T14:14:02.286Z', - 'entity.type': 'container', - 'entity.displayName': 'The Shadow', - 'entity.id': '453', - }, - { - 'entity.lastSeenTimestamp': '2024-03-31T23:20:56.580Z', - 'entity.type': 'host', - 'entity.displayName': 'Doc Savage', - 'entity.id': '454', - }, - { - 'entity.lastSeenTimestamp': '2023-05-18T01:28:20.743Z', - 'entity.type': 'container', - 'entity.displayName': 'Zorro', - 'entity.id': '455', - }, - { - 'entity.lastSeenTimestamp': '2023-01-12T01:19:03.220Z', - 'entity.type': 'service', - 'entity.displayName': 'The Phantom', - 'entity.id': '456', - }, - { - 'entity.lastSeenTimestamp': '2023-10-10T20:35:47.302Z', - 'entity.type': 'container', - 'entity.displayName': 'Green Hornet', - 'entity.id': '457', - }, - { - 'entity.lastSeenTimestamp': '2023-05-09T19:35:59.568Z', - 'entity.type': 'service', - 'entity.displayName': 'Kato', - 'entity.id': '458', - }, - { - 'entity.lastSeenTimestamp': '2023-07-02T19:40:18.206Z', - 'entity.type': 'host', - 'entity.displayName': 'Red Sonja', - 'entity.id': '459', - }, - { - 'entity.lastSeenTimestamp': '2024-01-08T20:03:24.184Z', - 'entity.type': 'container', - 'entity.displayName': 'Conan the Barbarian', - 'entity.id': '460', - }, - { - 'entity.lastSeenTimestamp': '2024-03-13T05:26:16.730Z', - 'entity.type': 'service', - 'entity.displayName': 'Homer Simpson', - 'entity.id': '461', - }, - { - 'entity.lastSeenTimestamp': '2024-06-28T02:49:37.987Z', - 'entity.type': 'host', - 'entity.displayName': 'Marge Simpson', - 'entity.id': '462', - }, - { - 'entity.lastSeenTimestamp': '2024-06-17T21:16:08.180Z', - 'entity.type': 'host', - 'entity.displayName': 'Bart Simpson', - 'entity.id': '463', - }, - { - 'entity.lastSeenTimestamp': '2023-03-27T21:34:38.051Z', - 'entity.type': 'host', - 'entity.displayName': 'Lisa Simpson', - 'entity.id': '464', - }, - { - 'entity.lastSeenTimestamp': '2023-02-04T21:08:36.340Z', - 'entity.type': 'service', - 'entity.displayName': 'Maggie Simpson', - 'entity.id': '465', - }, - { - 'entity.lastSeenTimestamp': '2024-05-22T20:05:45.805Z', - 'entity.type': 'service', - 'entity.displayName': 'Abe Simpson', - 'entity.id': '466', - }, - { - 'entity.lastSeenTimestamp': '2023-04-02T23:57:33.378Z', - 'entity.type': 'container', - 'entity.displayName': 'Ned Flanders', - 'entity.id': '467', - }, - { - 'entity.lastSeenTimestamp': '2023-03-05T12:25:19.985Z', - 'entity.type': 'container', - 'entity.displayName': 'Maude Flanders', - 'entity.id': '468', - }, - { - 'entity.lastSeenTimestamp': '2024-05-31T22:44:52.035Z', - 'entity.type': 'container', - 'entity.displayName': 'Rod Flanders', - 'entity.id': '469', - }, - { - 'entity.lastSeenTimestamp': '2024-03-06T22:07:45.916Z', - 'entity.type': 'container', - 'entity.displayName': 'Todd Flanders', - 'entity.id': '470', - }, - { - 'entity.lastSeenTimestamp': '2023-09-29T20:39:30.536Z', - 'entity.type': 'service', - 'entity.displayName': 'Milhouse Van Houten', - 'entity.id': '471', - }, - { - 'entity.lastSeenTimestamp': '2023-07-13T22:08:03.669Z', - 'entity.type': 'host', - 'entity.displayName': 'Nelson Muntz', - 'entity.id': '472', - }, - { - 'entity.lastSeenTimestamp': '2024-01-11T11:44:27.608Z', - 'entity.type': 'service', - 'entity.displayName': 'Ralph Wiggum', - 'entity.id': '473', - }, - { - 'entity.lastSeenTimestamp': '2023-10-07T03:48:20.334Z', - 'entity.type': 'container', - 'entity.displayName': 'Chief Wiggum', - 'entity.id': '474', - }, - { - 'entity.lastSeenTimestamp': '2023-12-26T00:46:10.602Z', - 'entity.type': 'host', - 'entity.displayName': 'Clancy Wiggum', - 'entity.id': '475', - }, - { - 'entity.lastSeenTimestamp': '2023-03-24T03:32:51.643Z', - 'entity.type': 'host', - 'entity.displayName': 'Krusty the Clown', - 'entity.id': '476', - }, - { - 'entity.lastSeenTimestamp': '2023-01-19T18:15:10.942Z', - 'entity.type': 'container', - 'entity.displayName': 'Sideshow Bob', - 'entity.id': '477', - }, - { - 'entity.lastSeenTimestamp': '2023-02-05T23:13:30.639Z', - 'entity.type': 'service', - 'entity.displayName': 'Sideshow Mel', - 'entity.id': '478', - }, - { - 'entity.lastSeenTimestamp': '2024-03-06T07:02:19.760Z', - 'entity.type': 'host', - 'entity.displayName': 'Moe Szyslak', - 'entity.id': '479', - }, - { - 'entity.lastSeenTimestamp': '2024-08-26T17:28:47.162Z', - 'entity.type': 'service', - 'entity.displayName': 'Barney Gumble', - 'entity.id': '480', - }, - { - 'entity.lastSeenTimestamp': '2024-05-12T12:10:32.668Z', - 'entity.type': 'service', - 'entity.displayName': 'Lenny Leonard', - 'entity.id': '481', - }, - { - 'entity.lastSeenTimestamp': '2023-07-25T05:19:12.244Z', - 'entity.type': 'service', - 'entity.displayName': 'Carl Carlson', - 'entity.id': '482', - }, - { - 'entity.lastSeenTimestamp': '2023-09-14T19:23:00.311Z', - 'entity.type': 'container', - 'entity.displayName': 'Waylon Smithers', - 'entity.id': '483', - }, - { - 'entity.lastSeenTimestamp': '2023-07-06T12:21:13.655Z', - 'entity.type': 'service', - 'entity.displayName': 'Mr. Burns', - 'entity.id': '484', - }, - { - 'entity.lastSeenTimestamp': '2023-01-23T07:14:22.901Z', - 'entity.type': 'service', - 'entity.displayName': 'Principal Skinner', - 'entity.id': '485', - }, - { - 'entity.lastSeenTimestamp': '2024-05-07T18:03:19.312Z', - 'entity.type': 'service', - 'entity.displayName': 'Edna Krabappel', - 'entity.id': '486', - }, - { - 'entity.lastSeenTimestamp': '2023-02-14T07:33:02.981Z', - 'entity.type': 'service', - 'entity.displayName': 'Superintendent Chalmers', - 'entity.id': '487', - }, - { - 'entity.lastSeenTimestamp': '2024-01-21T22:32:55.738Z', - 'entity.type': 'container', - 'entity.displayName': 'Groundskeeper Willie', - 'entity.id': '488', - }, - { - 'entity.lastSeenTimestamp': '2024-03-31T13:42:07.765Z', - 'entity.type': 'service', - 'entity.displayName': 'Otto Mann', - 'entity.id': '489', - }, - { - 'entity.lastSeenTimestamp': '2023-08-23T18:26:32.084Z', - 'entity.type': 'container', - 'entity.displayName': 'Apu Nahasapeemapetilon', - 'entity.id': '490', - }, - { - 'entity.lastSeenTimestamp': '2024-02-14T04:17:17.737Z', - 'entity.type': 'container', - 'entity.displayName': 'Manjula Nahasapeemapetilon', - 'entity.id': '491', - }, - { - 'entity.lastSeenTimestamp': '2024-07-06T03:25:46.939Z', - 'entity.type': 'service', - 'entity.displayName': 'Kearney Zzyzwicz', - 'entity.id': '492', - }, - { - 'entity.lastSeenTimestamp': '2023-09-04T06:08:42.239Z', - 'entity.type': 'service', - 'entity.displayName': 'Jimbo Jones', - 'entity.id': '493', - }, - { - 'entity.lastSeenTimestamp': '2023-06-12T23:45:21.630Z', - 'entity.type': 'host', - 'entity.displayName': 'Dolph Starbeam', - 'entity.id': '494', - }, - { - 'entity.lastSeenTimestamp': '2023-11-18T18:43:41.585Z', - 'entity.type': 'container', - 'entity.displayName': 'Martin Prince', - 'entity.id': '495', - }, - { - 'entity.lastSeenTimestamp': '2024-07-29T01:12:36.480Z', - 'entity.type': 'container', - 'entity.displayName': 'Mrs. Prince', - 'entity.id': '496', - }, - { - 'entity.lastSeenTimestamp': '2023-09-25T18:32:05.791Z', - 'entity.type': 'container', - 'entity.displayName': 'Comic Book Guy', - 'entity.id': '497', - }, - { - 'entity.lastSeenTimestamp': '2023-04-05T12:49:08.814Z', - 'entity.type': 'host', - 'entity.displayName': 'Professor Frink', - 'entity.id': '498', - }, - { - 'entity.lastSeenTimestamp': '2023-04-07T20:07:02.744Z', - 'entity.type': 'host', - 'entity.displayName': 'Troy McClure', - 'entity.id': '499', - }, -] as unknown as InventoryEntitiesAPIReturnType['entities']; +]; + +const hostsMock = Array.from({ length: 20 }, () => getEntity('host')); +const containersMock = Array.from({ length: 20 }, () => getEntity('container')); +const servicesMock = Array.from({ length: 20 }, () => getEntity('service')); + +export const entitiesMock = [ + ...alertsMock, + ...hostsMock, + ...containersMock, + ...servicesMock, +] as Entity[]; diff --git a/x-pack/plugins/observability_solution/inventory/tsconfig.json b/x-pack/plugins/observability_solution/inventory/tsconfig.json index c4c8f5d3ac59d..67de9919c6324 100644 --- a/x-pack/plugins/observability_solution/inventory/tsconfig.json +++ b/x-pack/plugins/observability_solution/inventory/tsconfig.json @@ -51,6 +51,7 @@ "@kbn/observability-plugin", "@kbn/rule-data-utils", "@kbn/spaces-plugin", - "@kbn/cloud-plugin" + "@kbn/cloud-plugin", + "@kbn/storybook" ] } diff --git a/x-pack/plugins/observability_solution/investigate_app/public/hooks/query_key_factory.ts b/x-pack/plugins/observability_solution/investigate_app/public/hooks/query_key_factory.ts index 0b625c5681bcb..38e4c90aebe09 100644 --- a/x-pack/plugins/observability_solution/investigate_app/public/hooks/query_key_factory.ts +++ b/x-pack/plugins/observability_solution/investigate_app/public/hooks/query_key_factory.ts @@ -12,8 +12,8 @@ export const investigationKeys = { userProfiles: (profileIds: Set) => [...investigationKeys.all, 'userProfiles', ...profileIds] as const, tags: () => [...investigationKeys.all, 'tags'] as const, - events: (rangeFrom?: string, rangeTo?: string) => - [...investigationKeys.all, 'events', rangeFrom, rangeTo] as const, + events: (rangeFrom?: string, rangeTo?: string, filter?: string) => + [...investigationKeys.all, 'events', rangeFrom, rangeTo, filter] as const, stats: () => [...investigationKeys.all, 'stats'] as const, lists: () => [...investigationKeys.all, 'list'] as const, list: (params: { page: number; perPage: number; search?: string; filter?: string }) => diff --git a/x-pack/plugins/observability_solution/investigate_app/public/hooks/use_fetch_events.ts b/x-pack/plugins/observability_solution/investigate_app/public/hooks/use_fetch_events.ts index 61b0c441c1fc2..5b885fc664b13 100644 --- a/x-pack/plugins/observability_solution/investigate_app/public/hooks/use_fetch_events.ts +++ b/x-pack/plugins/observability_solution/investigate_app/public/hooks/use_fetch_events.ts @@ -23,9 +23,11 @@ export interface Response { export function useFetchEvents({ rangeFrom, rangeTo, + filter, }: { rangeFrom?: string; rangeTo?: string; + filter?: string; }): Response { const { core: { @@ -35,12 +37,13 @@ export function useFetchEvents({ } = useKibana(); const { isInitialLoading, isLoading, isError, isSuccess, isRefetching, data } = useQuery({ - queryKey: investigationKeys.events(rangeFrom, rangeTo), + queryKey: investigationKeys.events(rangeFrom, rangeTo, filter), queryFn: async ({ signal }) => { return await http.get(`/api/observability/events`, { query: { rangeFrom, rangeTo, + filter, }, version: '2023-10-31', signal, diff --git a/x-pack/plugins/observability_solution/investigate_app/public/pages/details/components/events_timeline/events_timeline.tsx b/x-pack/plugins/observability_solution/investigate_app/public/pages/details/components/events_timeline/events_timeline.tsx index 70f4159924bd1..befa50bcc0e8d 100644 --- a/x-pack/plugins/observability_solution/investigate_app/public/pages/details/components/events_timeline/events_timeline.tsx +++ b/x-pack/plugins/observability_solution/investigate_app/public/pages/details/components/events_timeline/events_timeline.tsx @@ -11,6 +11,9 @@ import { Chart, Axis, AreaSeries, Position, ScaleType, Settings } from '@elastic import { useActiveCursor } from '@kbn/charts-plugin/public'; import { EuiSkeletonText } from '@elastic/eui'; import { getBrushData } from '@kbn/observability-utils/chart/utils'; +import { Group } from '@kbn/observability-alerting-rule-utils'; +import { ALERT_GROUP } from '@kbn/rule-data-utils'; +import { SERVICE_NAME } from '@kbn/observability-shared-plugin/common'; import { AnnotationEvent } from './annotation_event'; import { TIME_LINE_THEME } from './timeline_theme'; import { useFetchEvents } from '../../../../hooks/use_fetch_events'; @@ -24,10 +27,19 @@ export const EventsTimeLine = () => { const baseTheme = dependencies.start.charts.theme.useChartsBaseTheme(); const { globalParams, updateInvestigationParams } = useInvestigation(); + const { alert } = useInvestigation(); + + const filter = useMemo(() => { + const group = (alert?.[ALERT_GROUP] as unknown as Group[])?.find( + ({ field }) => field === SERVICE_NAME + ); + return group ? `{"${SERVICE_NAME}":"${alert?.[SERVICE_NAME]}"}` : ''; + }, [alert]); const { data: events, isLoading } = useFetchEvents({ rangeFrom: globalParams.timeRange.from, rangeTo: globalParams.timeRange.to, + filter, }); const chartRef = useRef(null); diff --git a/x-pack/plugins/observability_solution/investigate_app/server/services/get_events.ts b/x-pack/plugins/observability_solution/investigate_app/server/services/get_events.ts index 6b081f51dfee8..53f42f4c6c057 100644 --- a/x-pack/plugins/observability_solution/investigate_app/server/services/get_events.ts +++ b/x-pack/plugins/observability_solution/investigate_app/server/services/get_events.ts @@ -95,7 +95,7 @@ export async function getAlertEvents( id: _source[ALERT_UUID], title: `${_source[ALERT_RULE_CATEGORY]} breached`, description: _source[ALERT_REASON], - timestamp: new Date(_source['@timestamp']).getTime(), + timestamp: new Date(_source[ALERT_START] as string).getTime(), eventType: 'alert', alertStatus: _source[ALERT_STATUS], }; diff --git a/x-pack/plugins/observability_solution/investigate_app/tsconfig.json b/x-pack/plugins/observability_solution/investigate_app/tsconfig.json index 7ea8234fba670..a853456b1156c 100644 --- a/x-pack/plugins/observability_solution/investigate_app/tsconfig.json +++ b/x-pack/plugins/observability_solution/investigate_app/tsconfig.json @@ -68,5 +68,6 @@ "@kbn/ml-random-sampler-utils", "@kbn/charts-plugin", "@kbn/observability-utils", + "@kbn/observability-alerting-rule-utils", ], } diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx index f45a353be9a61..87aee8649300a 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx @@ -9,6 +9,7 @@ import React from 'react'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { coreMock as mockCoreMock } from '@kbn/core/public/mocks'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; +import { ALERT_RULE_PARAMETERS } from '@kbn/rule-data-utils'; import { ParsedTechnicalFields } from '@kbn/rule-registry-plugin/common'; import { render } from '@testing-library/react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; @@ -85,8 +86,10 @@ describe('AlertDetailsAppSection', () => { diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx index b474f246988b6..cc2f8364ce5b6 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx @@ -20,9 +20,14 @@ import { useEuiTheme, transparentize, } from '@elastic/eui'; -import { RuleTypeParams } from '@kbn/alerting-plugin/common'; import { getPaddedAlertTimeRange } from '@kbn/observability-get-padded-alert-time-range-util'; -import { ALERT_END, ALERT_START, ALERT_EVALUATION_VALUES, ALERT_GROUP } from '@kbn/rule-data-utils'; +import { + ALERT_END, + ALERT_START, + ALERT_EVALUATION_VALUES, + ALERT_GROUP, + ALERT_RULE_PARAMETERS, +} from '@kbn/rule-data-utils'; import { DataView } from '@kbn/data-views-plugin/common'; import type { EventAnnotationConfig, @@ -36,9 +41,8 @@ import { getGroupFilters } from '../../../../../common/custom_threshold_rule/hel import { useLicense } from '../../../../hooks/use_license'; import { useKibana } from '../../../../utils/kibana_react'; import { metricValueFormatter } from '../../../../../common/custom_threshold_rule/metric_value_formatter'; -import { AlertParams } from '../../types'; import { Threshold } from '../threshold'; -import { CustomThresholdRule, CustomThresholdAlert } from '../types'; +import { CustomThresholdAlert } from '../types'; import { LogRateAnalysis } from './log_rate_analysis'; import { RuleConditionChart } from '../../../rule_condition_chart/rule_condition_chart'; import { getViewInAppUrl } from '../../../../../common/custom_threshold_rule/get_view_in_app_url'; @@ -47,11 +51,10 @@ import { generateChartTitleAndTooltip } from './helpers/generate_chart_title_and interface AppSectionProps { alert: CustomThresholdAlert; - rule: CustomThresholdRule; } // eslint-disable-next-line import/no-default-export -export default function AlertDetailsAppSection({ alert, rule }: AppSectionProps) { +export default function AlertDetailsAppSection({ alert }: AppSectionProps) { const services = useKibana().services; const { charts, @@ -66,10 +69,10 @@ export default function AlertDetailsAppSection({ alert, rule }: AppSectionProps) const [dataView, setDataView] = useState(); const [, setDataViewError] = useState(); const [timeRange, setTimeRange] = useState({ from: 'now-15m', to: 'now' }); - const ruleParams = rule.params as RuleTypeParams & AlertParams; const chartProps = { baseTheme: charts.theme.useChartsBaseTheme(), }; + const ruleParams = alert.fields[ALERT_RULE_PARAMETERS]; const alertStart = alert.fields[ALERT_START]; const alertEnd = alert.fields[ALERT_END]; const groups = alert.fields[ALERT_GROUP]; @@ -213,7 +216,7 @@ export default function AlertDetailsAppSection({ alert, rule }: AppSectionProps) ); })} {hasLogRateAnalysisLicense && ( - + )} ); diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/log_rate_analysis_query.test.ts b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/log_rate_analysis_query.test.ts index 05a8c3eb67a35..996030cf890cf 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/log_rate_analysis_query.test.ts +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/log_rate_analysis_query.test.ts @@ -6,6 +6,7 @@ */ import { COMPARATORS } from '@kbn/alerting-comparators'; +import { ALERT_RULE_PARAMETERS } from '@kbn/rule-data-utils'; import { Aggregators } from '../../../../../../common/custom_threshold_rule/types'; import { CustomThresholdRuleTypeParams } from '../../../types'; import { getLogRateAnalysisEQQuery } from './log_rate_analysis_query'; @@ -50,74 +51,83 @@ describe('buildEsQuery', () => { }; const testData: Array<{ title: string; - params: CustomThresholdRuleTypeParams; alert: any; }> = [ { title: 'rule with optional filer, count filter and group by', - params: mockedParams, alert: { fields: { 'kibana.alert.group': [mockedAlertWithMultipleGroups.fields['kibana.alert.group'][0]], + [ALERT_RULE_PARAMETERS]: mockedParams, }, }, }, { title: 'rule with optional filer, count filter and multiple group by', - params: mockedParams, - alert: mockedAlertWithMultipleGroups, + alert: { + fields: { + ...mockedAlertWithMultipleGroups.fields, + [ALERT_RULE_PARAMETERS]: mockedParams, + }, + }, }, { title: 'rule with optional filer, count filter and WITHOUT group by', - params: mockedParams, - alert: {}, + alert: { + fields: { + [ALERT_RULE_PARAMETERS]: mockedParams, + }, + }, }, { title: 'rule without filter and with group by', - params: { - groupBy: ['host.hostname'], - searchConfiguration: { - index, - query: { query: '', language: 'kuery' }, - }, - criteria: [ - { - metrics: [{ name: 'A', aggType: Aggregators.COUNT }], - timeSize: 1, - timeUnit: 'm', - threshold: [90], - comparator: COMPARATORS.GREATER_THAN, - }, - ], - }, alert: { fields: { 'kibana.alert.group': [mockedAlertWithMultipleGroups.fields['kibana.alert.group'][0]], + [ALERT_RULE_PARAMETERS]: { + groupBy: ['host.hostname'], + searchConfiguration: { + index, + query: { query: '', language: 'kuery' }, + }, + criteria: [ + { + metrics: [{ name: 'A', aggType: Aggregators.COUNT }], + timeSize: 1, + timeUnit: 'm', + threshold: [90], + comparator: COMPARATORS.GREATER_THAN, + }, + ], + }, }, }, }, { title: 'rule with multiple metrics', - params: { - ...mockedParams, - criteria: [ - { - metrics: [ - { name: 'A', aggType: Aggregators.COUNT, filter: 'host.name: host-1' }, - { name: 'B', aggType: Aggregators.AVERAGE, field: 'system.load.1' }, + alert: { + fields: { + [ALERT_RULE_PARAMETERS]: { + ...mockedParams, + criteria: [ + { + metrics: [ + { name: 'A', aggType: Aggregators.COUNT, filter: 'host.name: host-1' }, + { name: 'B', aggType: Aggregators.AVERAGE, field: 'system.load.1' }, + ], + timeSize: 1, + timeUnit: 'm', + threshold: [90], + comparator: COMPARATORS.GREATER_THAN, + }, ], - timeSize: 1, - timeUnit: 'm', - threshold: [90], - comparator: COMPARATORS.GREATER_THAN, }, - ], + }, }, - alert: {}, }, ]; - test.each(testData)('should generate correct es query for $title', ({ alert, params }) => { - expect(getLogRateAnalysisEQQuery(alert, params)).toMatchSnapshot(); + test.each(testData)('should generate correct es query for $title', ({ alert }) => { + expect(getLogRateAnalysisEQQuery(alert)).toMatchSnapshot(); }); }); diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/log_rate_analysis_query.ts b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/log_rate_analysis_query.ts index 4bd0b16212e11..bea80bfb5ab5e 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/log_rate_analysis_query.ts +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/log_rate_analysis_query.ts @@ -7,12 +7,12 @@ import { get } from 'lodash'; import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; +import { ALERT_RULE_PARAMETERS } from '@kbn/rule-data-utils'; +import { CustomThresholdAlert } from '../../types'; import { getGroupFilters } from '../../../../../../common/custom_threshold_rule/helpers/get_group'; import { Aggregators } from '../../../../../../common/custom_threshold_rule/types'; import { buildEsQuery } from '../../../../../utils/build_es_query'; import type { CustomThresholdExpressionMetric } from '../../../../../../common/custom_threshold_rule/types'; -import type { TopAlert } from '../../../../../typings/alerts'; -import type { CustomThresholdRuleTypeParams } from '../../../types'; import { Group } from '../../../../../../common/typings'; const getKuery = (metrics: CustomThresholdExpressionMetric[], filter?: string) => { @@ -32,23 +32,23 @@ const getKuery = (metrics: CustomThresholdExpressionMetric[], filter?: string) = }; export const getLogRateAnalysisEQQuery = ( - alert: TopAlert>, - params: CustomThresholdRuleTypeParams + alert: CustomThresholdAlert ): QueryDslQueryContainer | undefined => { + const ruleParams = alert.fields[ALERT_RULE_PARAMETERS]; // We only show log rate analysis for one condition with one count aggregation if ( - params.criteria.length !== 1 || - params.criteria[0].metrics.length !== 1 || - params.criteria[0].metrics[0].aggType !== Aggregators.COUNT + ruleParams.criteria.length !== 1 || + ruleParams.criteria[0].metrics.length !== 1 || + ruleParams.criteria[0].metrics[0].aggType !== Aggregators.COUNT ) { return; } const group = get(alert, 'fields["kibana.alert.group"]') as Group[] | undefined; - const optionalFilter = get(params.searchConfiguration, 'query.query') as string | undefined; + const optionalFilter = get(ruleParams.searchConfiguration, 'query.query') as string | undefined; const groupByFilters = getGroupFilters(group); const boolQuery = buildEsQuery({ - kuery: getKuery(params.criteria[0].metrics, optionalFilter), + kuery: getKuery(ruleParams.criteria[0].metrics, optionalFilter), filters: groupByFilters, }); diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/log_rate_analysis.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/log_rate_analysis.tsx index f2285b3529f65..89e8cc5e2aa6a 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/log_rate_analysis.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/log_rate_analysis.tsx @@ -19,17 +19,14 @@ import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import type { Message } from '@kbn/observability-ai-assistant-plugin/public'; -import { Rule } from '@kbn/triggers-actions-ui-plugin/public'; -import { ALERT_END } from '@kbn/rule-data-utils'; -import { CustomThresholdRuleTypeParams } from '../../types'; -import { TopAlert } from '../../../..'; +import { ALERT_END, ALERT_RULE_PARAMETERS } from '@kbn/rule-data-utils'; +import { CustomThresholdAlert } from '../types'; import { Color, colorTransformer } from '../../../../../common/custom_threshold_rule/color_palette'; import { getLogRateAnalysisEQQuery } from './helpers/log_rate_analysis_query'; export interface AlertDetailsLogRateAnalysisProps { - alert: TopAlert>; + alert: CustomThresholdAlert; dataView: any; - rule: Rule; services: any; } @@ -40,12 +37,7 @@ interface SignificantFieldValue { pValue: number | null; } -export function LogRateAnalysis({ - alert, - dataView, - rule, - services, -}: AlertDetailsLogRateAnalysisProps) { +export function LogRateAnalysis({ alert, dataView, services }: AlertDetailsLogRateAnalysisProps) { const { observabilityAIAssistant: { ObservabilityAIAssistantContextualInsight, @@ -57,22 +49,23 @@ export function LogRateAnalysis({ | { logRateAnalysisType: LogRateAnalysisType; significantFieldValues: SignificantFieldValue[] } | undefined >(); + const ruleParams = alert.fields[ALERT_RULE_PARAMETERS]; useEffect(() => { - const esSearchRequest = getLogRateAnalysisEQQuery(alert, rule.params); + const esSearchRequest = getLogRateAnalysisEQQuery(alert); if (esSearchRequest) { setEsSearchQuery(esSearchRequest); } - }, [alert, rule.params]); + }, [alert]); const { timeRange, windowParameters } = useMemo(() => { const alertStartedAt = moment(alert.start).toISOString(); const alertEndedAt = alert.fields[ALERT_END] ? moment(alert.fields[ALERT_END]).toISOString() : undefined; - const timeSize = rule.params.criteria[0]?.timeSize as number | undefined; - const timeUnit = rule.params.criteria[0]?.timeUnit as + const timeSize = ruleParams.criteria[0]?.timeSize as number | undefined; + const timeUnit = ruleParams.criteria[0]?.timeUnit as | moment.unitOfTime.DurationConstructor | undefined; @@ -82,7 +75,7 @@ export function LogRateAnalysis({ timeSize, timeUnit, }); - }, [alert, rule]); + }, [alert.fields, alert.start, ruleParams.criteria]); const logRateAnalysisTitle = i18n.translate( 'xpack.observability.customThreshold.alertDetails.logRateAnalysisTitle', diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/mocks/custom_threshold_rule.ts b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/mocks/custom_threshold_rule.ts index c1db31d991c28..36f108b1db628 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/mocks/custom_threshold_rule.ts +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/mocks/custom_threshold_rule.ts @@ -227,19 +227,23 @@ export const buildCustomThresholdAlert = ( { name: 'B', aggType: Aggregators.MAX, - metric: 'system.cpu.user.pct', + field: 'system.cpu.user.pct', }, ], threshold: [4], timeSize: 15, timeUnit: 'm', - warningComparator: COMPARATORS.GREATER_THAN, - warningThreshold: [2.2], }, ], - sourceId: 'default', alertOnNoData: true, alertOnGroupDisappear: true, + searchConfiguration: { + query: { + query: '', + language: 'kuery', + }, + index: 'b3eadf0e-1053-41d0-9672-dc1d7789dd68', + }, }, 'kibana.alert.evaluation.values': [2500, 5], 'kibana.alert.group': [{ field: 'host.name', value: 'host-1' }], diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/types.ts b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/types.ts index 8d5b1260a809c..891661b6bc82a 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/types.ts +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/types.ts @@ -8,7 +8,7 @@ import * as rt from 'io-ts'; import { CasesPublicStart } from '@kbn/cases-plugin/public'; import { ChartsPluginStart } from '@kbn/charts-plugin/public'; -import { DataPublicPluginStart, SerializedSearchSourceFields } from '@kbn/data-plugin/public'; +import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { DataView, DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import { DiscoverStart } from '@kbn/discover-plugin/public'; import { EmbeddableStart } from '@kbn/embeddable-plugin/public'; @@ -16,7 +16,7 @@ import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import { LensPublicStart } from '@kbn/lens-plugin/public'; import { ObservabilitySharedPluginStart } from '@kbn/observability-shared-plugin/public'; import { OsqueryPluginStart } from '@kbn/osquery-plugin/public'; -import { ALERT_GROUP } from '@kbn/rule-data-utils'; +import { ALERT_GROUP, ALERT_RULE_PARAMETERS } from '@kbn/rule-data-utils'; import { SharePluginStart } from '@kbn/share-plugin/public'; import { SpacesPluginStart } from '@kbn/spaces-plugin/public'; import { @@ -87,11 +87,12 @@ export type RendererFunction = (args: Rende export interface CustomThresholdRuleTypeParams extends RuleTypeParams { criteria: CustomMetricExpressionParams[]; - searchConfiguration: SerializedSearchSourceFields; + searchConfiguration: CustomThresholdSearchSourceFields; groupBy?: string | string[]; } export interface CustomThresholdAlertFields { [ALERT_GROUP]?: Array<{ field: string; value: string }>; + [ALERT_RULE_PARAMETERS]: CustomThresholdRuleTypeParams; } export const expressionTimestampsRT = rt.type({ diff --git a/x-pack/plugins/observability_solution/observability/public/utils/investigation_item_helper.ts b/x-pack/plugins/observability_solution/observability/public/utils/investigation_item_helper.ts index cddf3290ed370..91bfcd2ab4bb1 100644 --- a/x-pack/plugins/observability_solution/observability/public/utils/investigation_item_helper.ts +++ b/x-pack/plugins/observability_solution/observability/public/utils/investigation_item_helper.ts @@ -24,9 +24,15 @@ const genLensEqForCustomThresholdRule = (criterion: MetricExpression) => { criterion.metrics.forEach((metric: CustomThresholdExpressionMetric) => { const metricFilter = metric.filter ? `kql='${metric.filter}'` : ''; - metricNameResolver[metric.name] = `${ - AggMappingForLens[metric.aggType] ? AggMappingForLens[metric.aggType] : metric.aggType - }(${metric.field ? metric.field : metricFilter})`; + if (metric.aggType === 'rate') { + metricNameResolver[metric.name] = `counter_rate(max(${ + metric.field ? metric.field : metricFilter + }))`; + } else { + metricNameResolver[metric.name] = `${ + AggMappingForLens[metric.aggType] ? AggMappingForLens[metric.aggType] : metric.aggType + }(${metric.field ? metric.field : metricFilter})`; + } }); let equation = criterion.equation diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/application/onboarding_flow_form/use_custom_cards_for_category.tsx b/x-pack/plugins/observability_solution/observability_onboarding/public/application/onboarding_flow_form/use_custom_cards_for_category.tsx index 10b2c52e441ee..0ef775d4e3f6c 100644 --- a/x-pack/plugins/observability_solution/observability_onboarding/public/application/onboarding_flow_form/use_custom_cards_for_category.tsx +++ b/x-pack/plugins/observability_solution/observability_onboarding/public/application/onboarding_flow_form/use_custom_cards_for_category.tsx @@ -61,12 +61,12 @@ export function useCustomCardsForCategory( } ), extraLabelsBadges: [ - + - , - + , + - , + , ], categories: ['observability'], icons: [ @@ -79,6 +79,7 @@ export function useCustomCardsForCategory( version: '', integration: '', isQuickstart: true, + release: 'preview', }, { id: 'otel-logs', @@ -98,12 +99,12 @@ export function useCustomCardsForCategory( } ), extraLabelsBadges: [ - + - , - + , + - , + , ], categories: ['observability'], icons: [ @@ -116,6 +117,7 @@ export function useCustomCardsForCategory( version: '', integration: '', isQuickstart: true, + release: 'preview', }, ]; @@ -139,9 +141,9 @@ export function useCustomCardsForCategory( } ), extraLabelsBadges: [ - + - , + , ], categories: ['observability'], icons: [ @@ -154,6 +156,7 @@ export function useCustomCardsForCategory( version: '', integration: '', isQuickstart: true, + release: 'preview', }, { id: 'otel-kubernetes', @@ -173,9 +176,9 @@ export function useCustomCardsForCategory( } ), extraLabelsBadges: [ - + - , + , ], categories: ['observability'], icons: [ @@ -188,6 +191,7 @@ export function useCustomCardsForCategory( version: '', integration: '', isQuickstart: true, + release: 'preview', }, ]; @@ -356,3 +360,11 @@ export function useCustomCardsForCategory( return undefined; } } + +function ExtraLabelBadgeWrapper({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/application/pages/auto_detect.tsx b/x-pack/plugins/observability_solution/observability_onboarding/public/application/pages/auto_detect.tsx index 585e1061291a5..7dc3d0acb0a2e 100644 --- a/x-pack/plugins/observability_solution/observability_onboarding/public/application/pages/auto_detect.tsx +++ b/x-pack/plugins/observability_solution/observability_onboarding/public/application/pages/auto_detect.tsx @@ -29,6 +29,7 @@ export const AutoDetectPage = () => ( 'This installation scans your host and auto-detects log and metric files.', } )} + isTechnicalPreview={true} /> } > diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/application/pages/kubernetes.tsx b/x-pack/plugins/observability_solution/observability_onboarding/public/application/pages/kubernetes.tsx index 8e1af954736c1..f92b1d9a83ac6 100644 --- a/x-pack/plugins/observability_solution/observability_onboarding/public/application/pages/kubernetes.tsx +++ b/x-pack/plugins/observability_solution/observability_onboarding/public/application/pages/kubernetes.tsx @@ -29,6 +29,7 @@ export const KubernetesPage = () => ( 'This installation is tailored for configuring and collecting metrics and logs by deploying a new Elastic Agent within your host.', } )} + isTechnicalPreview={true} /> } > diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/application/pages/otel_kubernetes.tsx b/x-pack/plugins/observability_solution/observability_onboarding/public/application/pages/otel_kubernetes.tsx index c4fba1fd8ff0e..8939a9fdf678e 100644 --- a/x-pack/plugins/observability_solution/observability_onboarding/public/application/pages/otel_kubernetes.tsx +++ b/x-pack/plugins/observability_solution/observability_onboarding/public/application/pages/otel_kubernetes.tsx @@ -28,6 +28,7 @@ export const OtelKubernetesPage = () => ( defaultMessage: 'Unified Kubernetes observability with the OpenTelemetry Operator', } )} + isTechnicalPreview={true} /> } > diff --git a/x-pack/plugins/observability_solution/observability_onboarding/server/lib/api_key/create_shipper_api_key.ts b/x-pack/plugins/observability_solution/observability_onboarding/server/lib/api_key/create_shipper_api_key.ts index bdfdd202a962e..9279ae0e1dfd1 100644 --- a/x-pack/plugins/observability_solution/observability_onboarding/server/lib/api_key/create_shipper_api_key.ts +++ b/x-pack/plugins/observability_solution/observability_onboarding/server/lib/api_key/create_shipper_api_key.ts @@ -6,7 +6,11 @@ */ import { ElasticsearchClient } from '@kbn/core/server'; -import { MONITOR_CLUSTER, INDEX_LOGS_AND_METRICS, WRITE_APM_EVENTS } from './privileges'; +import { + MONITOR_CLUSTER, + INDEX_LOGS_AND_METRICS, + INDEX_LOGS_METRICS_AND_TRACES, +} from './privileges'; export function createShipperApiKey(esClient: ElasticsearchClient, name: string, withAPM = false) { // Based on https://www.elastic.co/guide/en/fleet/master/grant-access-to-elasticsearch.html#create-api-key-standalone-agent @@ -20,8 +24,7 @@ export function createShipperApiKey(esClient: ElasticsearchClient, name: string, role_descriptors: { standalone_agent: { cluster: [MONITOR_CLUSTER], - indices: [INDEX_LOGS_AND_METRICS], - applications: withAPM ? [WRITE_APM_EVENTS] : undefined, + indices: [withAPM ? INDEX_LOGS_METRICS_AND_TRACES : INDEX_LOGS_AND_METRICS], }, }, }, diff --git a/x-pack/plugins/observability_solution/observability_onboarding/server/lib/api_key/has_log_monitoring_privileges.ts b/x-pack/plugins/observability_solution/observability_onboarding/server/lib/api_key/has_log_monitoring_privileges.ts index 0593a7f761e1e..ce5897936b741 100644 --- a/x-pack/plugins/observability_solution/observability_onboarding/server/lib/api_key/has_log_monitoring_privileges.ts +++ b/x-pack/plugins/observability_solution/observability_onboarding/server/lib/api_key/has_log_monitoring_privileges.ts @@ -6,14 +6,17 @@ */ import { ElasticsearchClient } from '@kbn/core/server'; -import { MONITOR_CLUSTER, INDEX_LOGS_AND_METRICS, WRITE_APM_EVENTS } from './privileges'; +import { + MONITOR_CLUSTER, + INDEX_LOGS_AND_METRICS, + INDEX_LOGS_METRICS_AND_TRACES, +} from './privileges'; export async function hasLogMonitoringPrivileges(esClient: ElasticsearchClient, withAPM = false) { const res = await esClient.security.hasPrivileges({ body: { cluster: [MONITOR_CLUSTER, 'manage_own_api_key'], - index: [INDEX_LOGS_AND_METRICS], - application: withAPM ? [WRITE_APM_EVENTS] : undefined, + index: [withAPM ? INDEX_LOGS_METRICS_AND_TRACES : INDEX_LOGS_AND_METRICS], }, }); diff --git a/x-pack/plugins/observability_solution/observability_onboarding/server/lib/api_key/privileges.ts b/x-pack/plugins/observability_solution/observability_onboarding/server/lib/api_key/privileges.ts index 7c3b5999842bd..8a28849ef1003 100644 --- a/x-pack/plugins/observability_solution/observability_onboarding/server/lib/api_key/privileges.ts +++ b/x-pack/plugins/observability_solution/observability_onboarding/server/lib/api_key/privileges.ts @@ -18,9 +18,8 @@ export const INDEX_LOGS_AND_METRICS: estypes.SecurityIndicesPrivileges = { privileges: ['auto_configure', 'create_doc'], }; -// https://www.elastic.co/guide/en/observability/master/apm-api-key.html#apm-create-api-key-workflow-es -export const WRITE_APM_EVENTS: estypes.SecurityApplicationPrivileges = { - application: 'apm', - privileges: ['event:write', 'config_agent:read'], - resources: ['*'], +// https://www.elastic.co/guide/en/fleet/master/grant-access-to-elasticsearch.html#create-api-key-standalone-agent +export const INDEX_LOGS_METRICS_AND_TRACES: estypes.SecurityIndicesPrivileges = { + names: ['logs-*-*', 'metrics-*-*', 'traces-*-*'], + privileges: ['auto_configure', 'create_doc'], }; diff --git a/x-pack/plugins/osquery/cypress/e2e/all/alerts_automated_action_results.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/alerts_automated_action_results.cy.ts index 31bcd68da19cd..1bc058b188fcc 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/alerts_automated_action_results.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/alerts_automated_action_results.cy.ts @@ -11,7 +11,7 @@ import { checkActionItemsInResults, loadRuleAlerts } from '../../tasks/live_quer const UUID_REGEX = '[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}'; -// Failing: See https://github.com/elastic/kibana/issues/178404 +// FLAKY: https://github.com/elastic/kibana/issues/169727 describe.skip('Alert Flyout Automated Action Results', () => { let ruleId: string; diff --git a/x-pack/plugins/osquery/cypress/e2e/all/alerts_cases.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/alerts_cases.cy.ts index ca0692b310606..a5ab52a7bdb76 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/alerts_cases.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/alerts_cases.cy.ts @@ -48,7 +48,8 @@ describe('Alert Event Details - Cases', { tags: ['@ess', '@serverless'] }, () => cleanupRule(ruleId); }); - describe('Case creation', () => { + // FLAKY: https://github.com/elastic/kibana/issues/197151 + describe.skip('Case creation', () => { let caseId: string; before(() => { @@ -84,7 +85,7 @@ describe('Alert Event Details - Cases', { tags: ['@ess', '@serverless'] }, () => }); }); - // FLAKY: https://github.com/elastic/kibana/issues/187182 + // FLAKY: https://github.com/elastic/kibana/issues/176783 describe.skip('Case', () => { let caseId: string; diff --git a/x-pack/plugins/osquery/cypress/e2e/all/alerts_linked_apps.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/alerts_linked_apps.cy.ts index 92b174583dd1d..f7585d32a2bba 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/alerts_linked_apps.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/alerts_linked_apps.cy.ts @@ -18,7 +18,9 @@ import { import { closeModalIfVisible, closeToastIfVisible } from '../../tasks/integrations'; import { RESULTS_TABLE, RESULTS_TABLE_BUTTON } from '../../screens/live_query'; -describe( +// Failing: See https://github.com/elastic/kibana/issues/181889 +// Failing: See https://github.com/elastic/kibana/issues/181889 +describe.skip( 'Alert Event Details', { tags: ['@ess', '@serverless', '@skipInServerlessMKI'], @@ -26,10 +28,8 @@ describe( () => { let ruleId: string; let ruleName: string; - before(() => { - initializeDataViews(); - }); beforeEach(() => { + initializeDataViews(); loadRule().then((data) => { ruleId = data.id; ruleName = data.name; diff --git a/x-pack/plugins/osquery/cypress/e2e/all/alerts_multiple_agents.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/alerts_multiple_agents.cy.ts index 95f0d947b8e84..4f6d30dd71431 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/alerts_multiple_agents.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/alerts_multiple_agents.cy.ts @@ -15,7 +15,8 @@ import { } from '../../tasks/live_query'; import { OSQUERY_FLYOUT_BODY_EDITOR } from '../../screens/live_query'; -describe( +// FLAKY: https://github.com/elastic/kibana/issues/170157 +describe.skip( 'Alert Event Details - dynamic params', { tags: ['@ess', '@serverless'], diff --git a/x-pack/plugins/osquery/cypress/e2e/all/live_query.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/live_query.cy.ts index cafc97cb646ea..6f551ad39b196 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/live_query.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/live_query.cy.ts @@ -19,8 +19,7 @@ import { LIVE_QUERY_EDITOR } from '../../screens/live_query'; import { getAdvancedButton } from '../../screens/integrations'; import { ServerlessRoleName } from '../../support/roles'; -// Failing: See https://github.com/elastic/kibana/issues/171435 -describe.skip('ALL - Live Query', { tags: ['@ess', '@serverless'] }, () => { +describe('ALL - Live Query', { tags: ['@ess', '@serverless'] }, () => { beforeEach(() => { cy.login(ServerlessRoleName.SOC_MANAGER); navigateTo('/app/osquery'); diff --git a/x-pack/plugins/osquery/cypress/e2e/all/live_query_run.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/live_query_run.cy.ts index f0a19907d57d8..c74b253ae9d41 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/live_query_run.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/live_query_run.cy.ts @@ -23,8 +23,7 @@ import { getAdvancedButton } from '../../screens/integrations'; import { loadSavedQuery, cleanupSavedQuery } from '../../tasks/api_fixtures'; import { ServerlessRoleName } from '../../support/roles'; -// FLAKY: https://github.com/elastic/kibana/issues/195458 -describe.skip('ALL - Live Query run custom and saved', { tags: ['@ess', '@serverless'] }, () => { +describe('ALL - Live Query run custom and saved', { tags: ['@ess', '@serverless'] }, () => { let savedQueryId: string; let savedQueryName: string; diff --git a/x-pack/plugins/osquery/cypress/e2e/all/packs_create_edit.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/packs_create_edit.cy.ts index 39a54d29d4025..dbed4e56f88a0 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/packs_create_edit.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/packs_create_edit.cy.ts @@ -38,8 +38,7 @@ import { loadSavedQuery, cleanupSavedQuery, cleanupPack, loadPack } from '../../ import { request } from '../../tasks/common'; import { ServerlessRoleName } from '../../support/roles'; -// Failing: See https://github.com/elastic/kibana/issues/195463 -describe.skip('Packs - Create and Edit', { tags: ['@ess', '@serverless'] }, () => { +describe('Packs - Create and Edit', { tags: ['@ess', '@serverless'] }, () => { let savedQueryId: string; let savedQueryName: string; let nomappingSavedQueryId: string; diff --git a/x-pack/plugins/osquery/cypress/e2e/all/saved_queries.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/saved_queries.cy.ts index d74bd2483f746..8076aab1c49cc 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/saved_queries.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/saved_queries.cy.ts @@ -43,9 +43,7 @@ import { import { ServerlessRoleName } from '../../support/roles'; import { getAdvancedButton } from '../../screens/integrations'; -// Failing: See https://github.com/elastic/kibana/issues/195453 -// Failing: See https://github.com/elastic/kibana/issues/195453 -describe.skip('ALL - Saved queries', { tags: ['@ess', '@serverless'] }, () => { +describe('ALL - Saved queries', { tags: ['@ess', '@serverless'] }, () => { let caseId: string; before(() => { @@ -94,20 +92,18 @@ describe.skip('ALL - Saved queries', { tags: ['@ess', '@serverless'] }, () => { cy.getBySel(RESULTS_TABLE_COLUMNS_BUTTON).should('have.text', 'Columns35'); cy.getBySel('dataGridColumnSelectorButton').click(); cy.get('[data-popover-open="true"]').should('be.visible'); - cy.getBySel('dataGridColumnSelectorToggleColumnVisibility-osquery.cmdline').click(); - cy.getBySel('dataGridColumnSelectorToggleColumnVisibility-osquery.cwd').click(); - cy.getBySel( - 'dataGridColumnSelectorToggleColumnVisibility-osquery.disk_bytes_written.number' - ).click(); + cy.getBySel('dataGridColumnSelectorColumnItem-osquery.cmdline').click(); + cy.getBySel('dataGridColumnSelectorColumnItem-osquery.cwd').click(); + cy.getBySel('dataGridColumnSelectorColumnItem-osquery.disk_bytes_written.number').click(); cy.getBySel('dataGridColumnSelectorButton').click(); cy.get('[data-popover-open="true"]').should('not.exist'); - cy.getBySel(RESULTS_TABLE_COLUMNS_BUTTON).should('have.text', 'Columns32/35'); + cy.getBySel(RESULTS_TABLE_COLUMNS_BUTTON).should('have.text', 'Columns35'); // change pagination cy.getBySel('pagination-button-next').click(); cy.getBySel('globalLoadingIndicator').should('not.exist'); cy.getBySel('pagination-button-next').click(); - cy.getBySel(RESULTS_TABLE_COLUMNS_BUTTON).should('have.text', 'Columns32/35'); + cy.getBySel(RESULTS_TABLE_COLUMNS_BUTTON).should('have.text', 'Columns35'); // enter fullscreen cy.getBySel(RESULTS_TABLE_BUTTON).trigger('mouseover'); @@ -118,7 +114,7 @@ describe.skip('ALL - Saved queries', { tags: ['@ess', '@serverless'] }, () => { // sorting cy.getBySel('dataGridHeaderCellActionButton-osquery.egid').click({ force: true }); cy.contains(/Sort A-Z$/).click(); - cy.getBySel(RESULTS_TABLE_COLUMNS_BUTTON).should('have.text', 'Columns32/35'); + cy.getBySel(RESULTS_TABLE_COLUMNS_BUTTON).should('have.text', 'Columns35'); cy.getBySel(RESULTS_TABLE_BUTTON).trigger('mouseover'); cy.contains(/Enter fullscreen$/).should('exist'); diff --git a/x-pack/plugins/remote_clusters/__jest__/client_integration/add/remote_clusters_add.test.ts b/x-pack/plugins/remote_clusters/__jest__/client_integration/add/remote_clusters_add.test.ts index 43157fa157f0b..6e6f5d7692b75 100644 --- a/x-pack/plugins/remote_clusters/__jest__/client_integration/add/remote_clusters_add.test.ts +++ b/x-pack/plugins/remote_clusters/__jest__/client_integration/add/remote_clusters_add.test.ts @@ -11,6 +11,7 @@ import { act } from 'react-dom/test-utils'; import { setupEnvironment, RemoteClustersActions } from '../helpers'; import { setup } from './remote_clusters_add.helpers'; import { NON_ALPHA_NUMERIC_CHARS, ACCENTED_CHARS } from './special_characters'; +import { MAX_NODE_CONNECTIONS } from '../../../common/constants'; const notInArray = (array: string[]) => (value: string) => array.indexOf(value) < 0; @@ -276,6 +277,17 @@ describe('Create Remote cluster', () => { }); }); + describe('node connections', () => { + test('should require a valid number of node connections', async () => { + await actions.saveButton.click(); + + actions.nodeConnectionsInput.setValue(String(MAX_NODE_CONNECTIONS + 1)); + expect(actions.getErrorMessages()).toContain( + `This number must be equal or less than ${MAX_NODE_CONNECTIONS}.` + ); + }); + }); + test('server name is optional (proxy connection)', () => { actions.connectionModeSwitch.toggle(); actions.saveButton.click(); diff --git a/x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_actions.ts b/x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_actions.ts index f657058231a84..00e33def31ef6 100644 --- a/x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_actions.ts +++ b/x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_actions.ts @@ -42,6 +42,9 @@ export interface RemoteClustersActions { setValue: (seed: string) => void; getValue: () => string; }; + nodeConnectionsInput: { + setValue: (connections: string) => void; + }; proxyAddressInput: { setValue: (proxyAddress: string) => void; exists: () => boolean; @@ -154,6 +157,16 @@ export const createRemoteClustersActions = (testBed: TestBed): RemoteClustersAct }; }; + const createNodeConnectionsInputActions = () => { + const nodeConnectionsInputSelector = 'remoteClusterFormNodeConnectionsInput'; + return { + nodeConnectionsInput: { + setValue: (connections: string) => + form.setInputValue(nodeConnectionsInputSelector, connections), + }, + }; + }; + const createProxyAddressActions = () => { const proxyAddressSelector = 'remoteClusterFormProxyAddressInput'; return { @@ -266,6 +279,7 @@ export const createRemoteClustersActions = (testBed: TestBed): RemoteClustersAct ...createConnectionModeActions(), ...createCloudAdvancedOptionsSwitchActions(), ...createSeedsInputActions(), + ...createNodeConnectionsInputActions(), ...createCloudRemoteAddressInputActions(), ...createProxyAddressActions(), ...createServerNameActions(), diff --git a/x-pack/plugins/remote_clusters/common/constants.ts b/x-pack/plugins/remote_clusters/common/constants.ts index 1357de2cd4640..889b5afc7e1fc 100644 --- a/x-pack/plugins/remote_clusters/common/constants.ts +++ b/x-pack/plugins/remote_clusters/common/constants.ts @@ -42,3 +42,6 @@ export const getSecurityModel = (type: string) => { return type; }; + +// Hardcoded limit of maximum node connections allowed +export const MAX_NODE_CONNECTIONS = 2 ** 31 - 1; // 2147483647 diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/components/sniff_connection.tsx b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/components/sniff_connection.tsx index 2d1608bcc4a70..c29a1ce5c2169 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/components/sniff_connection.tsx +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/components/sniff_connection.tsx @@ -28,14 +28,16 @@ export const SniffConnection: FunctionComponent = ({ }) => { const [localSeedErrors, setLocalSeedErrors] = useState([]); const { seeds = [], nodeConnections } = fields; - const { seeds: seedsError } = fieldsErrors; + const { seeds: seedsError, nodeConnections: nodeError } = fieldsErrors; // Show errors if there is a general form error or local errors. const areFormErrorsVisible = Boolean(areErrorsVisible && seedsError); - const showErrors = areFormErrorsVisible || localSeedErrors.length !== 0; - const errors = + const showLocalSeedErrors = areFormErrorsVisible || localSeedErrors.length !== 0; + const errorsInLocalSeeds = areFormErrorsVisible && seedsError ? localSeedErrors.concat(seedsError) : localSeedErrors; const formattedSeeds: EuiComboBoxOptionOption[] = seeds.map((seed: string) => ({ label: seed })); + const showNodeConnectionErrors = Boolean(nodeError); + const onCreateSeed = (newSeed?: string) => { // If the user just hit enter without typing anything, treat it as a no-op. if (!newSeed) { @@ -107,8 +109,8 @@ export const SniffConnection: FunctionComponent = ({ }} /> } - isInvalid={showErrors} - error={errors} + isInvalid={showLocalSeedErrors} + error={errorsInLocalSeeds} fullWidth > = ({ onFieldsChange({ seeds: options.map(({ label }) => label) }) } onSearchChange={onSeedsInputChange} - isInvalid={showErrors} + isInvalid={showLocalSeedErrors} fullWidth data-test-subj="remoteClusterFormSeedsInput" /> @@ -146,11 +148,15 @@ export const SniffConnection: FunctionComponent = ({ /> } fullWidth + isInvalid={showNodeConnectionErrors} + error={nodeError} > onFieldsChange({ nodeConnections: Number(e.target.value) })} + isInvalid={showNodeConnectionErrors} fullWidth + data-test-subj="remoteClusterFormNodeConnectionsInput" /> diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/__snapshots__/validate_node_connections.test.tsx.snap b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/__snapshots__/validate_node_connections.test.tsx.snap new file mode 100644 index 0000000000000..0c1ec0f1db18c --- /dev/null +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/__snapshots__/validate_node_connections.test.tsx.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`validateNodeConnections rejects numbers larger than MaxValue 1`] = ` + +`; diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/index.ts b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/index.ts index 6a710dae744ba..1fca3c5e83a5c 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/index.ts +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/index.ts @@ -16,3 +16,4 @@ export { validateCloudRemoteAddress, convertCloudRemoteAddressToProxyConnection, } from './validate_cloud_url'; +export { validateNodeConnections } from './validate_node_connections'; diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_cluster.tsx b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_cluster.tsx index aba0b0462cdf5..6d3f9e31c74b6 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_cluster.tsx +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_cluster.tsx @@ -11,6 +11,7 @@ import { validateSeeds } from './validate_seeds'; import { validateProxy } from './validate_proxy'; import { validateCloudRemoteAddress } from './validate_cloud_url'; import { FormFields } from '../remote_cluster_form'; +import { validateNodeConnections } from './validate_node_connections'; type ClusterError = JSX.Element | null; @@ -19,14 +20,16 @@ export interface ClusterErrors { seeds?: ClusterError; proxyAddress?: ClusterError; cloudRemoteAddress?: ClusterError; + nodeConnections?: ClusterError; } export const validateCluster = (fields: FormFields, isCloudEnabled: boolean): ClusterErrors => { - const { name, seeds = [], mode, proxyAddress, cloudRemoteAddress } = fields; + const { name, seeds = [], mode, proxyAddress, cloudRemoteAddress, nodeConnections } = fields; return { name: validateName(name), seeds: mode === SNIFF_MODE ? validateSeeds(seeds) : null, proxyAddress: mode === PROXY_MODE ? validateProxy(proxyAddress) : null, cloudRemoteAddress: isCloudEnabled ? validateCloudRemoteAddress(cloudRemoteAddress) : null, + nodeConnections: mode === SNIFF_MODE ? validateNodeConnections(nodeConnections) : null, }; }; diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_node_connections.test.tsx b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_node_connections.test.tsx new file mode 100644 index 0000000000000..20f39395692b7 --- /dev/null +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_node_connections.test.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 { MAX_NODE_CONNECTIONS } from '../../../../../../common/constants'; +import { validateNodeConnections } from './validate_node_connections'; + +describe('validateNodeConnections', () => { + test('rejects numbers larger than MaxValue', () => { + expect(validateNodeConnections(MAX_NODE_CONNECTIONS + 1)).toMatchSnapshot(); + }); + + test('accepts numbers equal than MaxValue', () => { + expect(validateNodeConnections(MAX_NODE_CONNECTIONS)).toBe(null); + }); + + test('accepts numbers smaller than MaxValue', () => { + expect(validateNodeConnections(MAX_NODE_CONNECTIONS - 1)).toBe(null); + }); +}); diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_node_connections.tsx b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_node_connections.tsx new file mode 100644 index 0000000000000..4adadb6fc1d6d --- /dev/null +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_node_connections.tsx @@ -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 React from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { MAX_NODE_CONNECTIONS } from '../../../../../../common/constants'; + +export function validateNodeConnections(connections?: number | null): null | JSX.Element { + if (connections && connections > MAX_NODE_CONNECTIONS) { + return ( + + ); + } + + return null; +} diff --git a/x-pack/plugins/reporting/public/management/components/report_info_flyout.tsx b/x-pack/plugins/reporting/public/management/components/report_info_flyout.tsx index b503fc11ab360..c506ddec00029 100644 --- a/x-pack/plugins/reporting/public/management/components/report_info_flyout.tsx +++ b/x-pack/plugins/reporting/public/management/components/report_info_flyout.tsx @@ -139,7 +139,7 @@ export const ReportInfoFlyout: FunctionComponent = ({ config, onClose, jo {isLoading ? ( ) : loadingError ? undefined : !!info ? ( - + ) : undefined} {!isLoading && ( diff --git a/x-pack/plugins/reporting/public/management/components/report_info_flyout_content.tsx b/x-pack/plugins/reporting/public/management/components/report_info_flyout_content.tsx index 33d81949a06bf..6820d22db8671 100644 --- a/x-pack/plugins/reporting/public/management/components/report_info_flyout_content.tsx +++ b/x-pack/plugins/reporting/public/management/components/report_info_flyout_content.tsx @@ -18,7 +18,7 @@ import { import { i18n } from '@kbn/i18n'; import { VisualReportingSoftDisabledError } from '@kbn/reporting-common/errors'; -import { Job, useKibana } from '@kbn/reporting-public'; +import { ClientConfigType, Job, useKibana } from '@kbn/reporting-public'; import { USES_HEADLESS_JOB_TYPES } from '../../../common/constants'; import { sharedI18nTexts } from '../../shared_i18n_texts'; @@ -33,6 +33,7 @@ const UNKNOWN = i18n.translate('xpack.reporting.listing.infoPanel.unknownLabel', interface Props { info: Job; + config: ClientConfigType; } const createDateFormatter = (format: string, tz: string) => (date: string) => { @@ -40,7 +41,7 @@ const createDateFormatter = (format: string, tz: string) => (date: string) => { return m.isValid() ? m.format(format) : NA; }; -export const ReportInfoFlyoutContent: FunctionComponent = ({ info }) => { +export const ReportInfoFlyoutContent: FunctionComponent = ({ info, config }) => { const { services: { uiSettings, docLinks }, } = useKibana(); @@ -50,6 +51,8 @@ export const ReportInfoFlyoutContent: FunctionComponent = ({ info }) => { ? moment.tz.guess() : uiSettings.get('dateFormat:tz'); + const showKibanaVersion = Boolean(info.version) && config.statefulSettings.enabled; + const formatDate = createDateFormatter(uiSettings.get('dateFormat'), timezone); const formatMilliseconds = (millis: number) => i18n.translate('xpack.reporting.listing.infoPanel.msToSeconds', { @@ -74,7 +77,7 @@ export const ReportInfoFlyoutContent: FunctionComponent = ({ info }) => { }), description: info.prettyStatus, }, - Boolean(info.version) && { + showKibanaVersion && { title: i18n.translate('xpack.reporting.listing.infoPanel.kibanaVersion', { defaultMessage: 'Kibana version', }), diff --git a/x-pack/plugins/saved_objects_tagging/public/utils.ts b/x-pack/plugins/saved_objects_tagging/public/utils.ts index 38ae79f3ca033..fc8ec8ebd3029 100644 --- a/x-pack/plugins/saved_objects_tagging/public/utils.ts +++ b/x-pack/plugins/saved_objects_tagging/public/utils.ts @@ -45,7 +45,7 @@ export const getTagsFromReferences = (references: SavedObjectReference[], allTag }; export const convertTagNameToId = (tagName: string, allTags: Tag[]): string | undefined => { - const found = allTags.find((tag) => tag.name === tagName); + const found = allTags.find((tag) => tag.name.toLowerCase() === tagName.toLowerCase()); return found?.id; }; diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/pdfmaker.ts b/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/pdfmaker.ts index 2eda655509f71..65765d8fc78c8 100644 --- a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/pdfmaker.ts +++ b/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/pdfmaker.ts @@ -22,7 +22,12 @@ import { } from './constants'; import { REPORTING_TABLE_LAYOUT } from './get_doc_options'; import { getFont } from './get_font'; -import type { GeneratePdfRequest, GeneratePdfResponse, WorkerData } from './worker'; +import { + GeneratePdfResponseType, + type GeneratePdfRequest, + type WorkerData, + GeneratePdfResponse, +} from './worker'; // Ensure that all dependencies are included in the release bundle. import './worker_dependencies'; @@ -32,6 +37,8 @@ export class PdfMaker { private content: Content[]; private worker?: Worker; + private workerLogger: Logger; + private pageCount: number = 0; private transferList: ArrayBuffer[] = []; @@ -71,6 +78,7 @@ export class PdfMaker { ) { this.title = ''; this.content = []; + this.workerLogger = logger.get('pdf-worker'); // running in dist: `worker.ts` becomes `worker.js` // running in source: `worker_src_harness.ts` needs to be wrapped in JS and have a ts-node environment initialized. @@ -209,26 +217,37 @@ export class PdfMaker { const { port1: myPort, port2: theirPort } = new MessageChannel(); this.worker = this.createWorker(theirPort); this.worker.on('error', (workerError: NodeJS.ErrnoException) => { + this.workerLogger.error(`Worker error: ${workerError}`); if (workerError.code === 'ERR_WORKER_OUT_OF_MEMORY') { reject(new errors.PdfWorkerOutOfMemoryError(workerError.message)); } else { reject(workerError); } }); - this.worker.on('exit', () => {}); + this.worker.on('exit', () => { + this.workerLogger.debug('Worker exited'); + }); - // We expect one message from the worker generating the PDF buffer. - myPort.on('message', ({ error, data }: GeneratePdfResponse) => { - if (error) { + myPort.on('message', ({ type, error, data, message }: GeneratePdfResponse) => { + if (type === GeneratePdfResponseType.Log && message) { + this.workerLogger.debug(message); + return; + } + + if (type === GeneratePdfResponseType.Error) { reject(new Error(`PDF worker returned the following error: ${error}`)); return; } - if (!data) { + + if (type === GeneratePdfResponseType.Data && !data) { reject(new Error(`Worker did not generate a PDF!`)); return; } - this.pageCount = data.metrics.pages; - resolve(data.buffer); + + if (data) { + this.pageCount = data.metrics.pages; + resolve(data.buffer); + } }); // Send the request diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/worker.ts b/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/worker.ts index 98c10598703ae..2599a57cdac97 100644 --- a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/worker.ts +++ b/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/worker.ts @@ -34,23 +34,44 @@ export interface GeneratePdfRequest { data: GenerateReportRequestData; } -export type GeneratePdfResponse = SuccessResponse | ErrorResponse; - -export interface SuccessResponse { - error?: undefined; - data: { - buffer: Uint8Array; - metrics: { - pages: number; - }; +export interface GeneratePdfData { + buffer: Uint8Array; + metrics: { + pages: number; }; } -export interface ErrorResponse { - error: string; - data: null; +export enum GeneratePdfResponseType { + Log, + Data, + Error, +} + +interface GeneratePdfLogResponse { + type: GeneratePdfResponseType.Log; + data?: GeneratePdfData; + error?: string; + message?: string; +} + +interface GeneratePdfDataResponse { + type: GeneratePdfResponseType.Data; + data?: GeneratePdfData; + error?: string; + message?: string; } +interface GeneratePdfErrorResponse { + type: GeneratePdfResponseType.Error; + data?: GeneratePdfData; + error?: string; + message?: string; +} +export type GeneratePdfResponse = + | GeneratePdfLogResponse + | GeneratePdfDataResponse + | GeneratePdfErrorResponse; + if (!isMainThread) { const { port } = workerData as WorkerData; port.on('message', execute); @@ -68,6 +89,11 @@ const getPageCount = (pdfDoc: PDFKit.PDFDocument): number => { async function execute({ data: { layout, logo, title, content } }: GeneratePdfRequest) { const { port } = workerData as WorkerData; + port.postMessage({ + type: GeneratePdfResponseType.Log, + message: 'Starting execution', + }); + try { const tableBorderWidth = 1; @@ -89,6 +115,11 @@ async function execute({ data: { layout, logo, title, content } }: GeneratePdfRe }, }; + port.postMessage({ + type: GeneratePdfResponseType.Log, + message: 'Initializing PDF printer', + }); + const printer = new Printer(fonts); const docDefinition = _.assign(getTemplate(layout, logo, title, tableBorderWidth, assetPath), { @@ -103,6 +134,11 @@ async function execute({ data: { layout, logo, title, content } }: GeneratePdfRe ), }); + port.postMessage({ + type: GeneratePdfResponseType.Log, + message: 'Generating document stream', + }); + const pdfDoc = printer.createPdfKitDocument(docDefinition, { tableLayouts: { noBorder: { @@ -121,6 +157,11 @@ async function execute({ data: { layout, logo, title, content } }: GeneratePdfRe throw new Error('Document stream has not been generated'); } + port.postMessage({ + type: GeneratePdfResponseType.Log, + message: 'Document stream has been generated', + }); + const buffer = await new Promise((resolve, reject) => { const buffers: Buffer[] = []; pdfDoc.on('error', reject); @@ -133,7 +174,13 @@ async function execute({ data: { layout, logo, title, content } }: GeneratePdfRe pdfDoc.end(); }); - const successResponse: SuccessResponse = { + port.postMessage({ + type: GeneratePdfResponseType.Log, + message: 'PDF buffer has been generated', + }); + + const successResponse: GeneratePdfResponse = { + type: GeneratePdfResponseType.Data, data: { buffer, metrics: { @@ -143,7 +190,10 @@ async function execute({ data: { layout, logo, title, content } }: GeneratePdfRe }; port.postMessage(successResponse, [buffer.buffer /* Transfer buffer instead of copying */]); } catch (error) { - const errorResponse: ErrorResponse = { error: error.message, data: null }; + const errorResponse: GeneratePdfResponse = { + type: GeneratePdfResponseType.Error, + error: error.message, + }; port.postMessage(errorResponse); } finally { process.nextTick(() => { diff --git a/x-pack/plugins/search_indices/public/components/indices/details_page.tsx b/x-pack/plugins/search_indices/public/components/indices/details_page.tsx index e8868663a9a3f..ad5e174dd6e4a 100644 --- a/x-pack/plugins/search_indices/public/components/indices/details_page.tsx +++ b/x-pack/plugins/search_indices/public/components/indices/details_page.tsx @@ -190,7 +190,7 @@ export const SearchIndexDetailsPage = () => { }, [isShowingDeleteModal]); const { euiTheme } = useEuiTheme(); - if (isInitialLoading || isMappingsInitialLoading) { + if (isInitialLoading || isMappingsInitialLoading || indexDocumentsIsInitialLoading) { return ( {i18n.translate('xpack.searchIndices.loadingDescription', { @@ -209,7 +209,7 @@ export const SearchIndexDetailsPage = () => { panelled bottomBorder > - {isIndexError || isMappingsError || !index || !mappings ? ( + {isIndexError || isMappingsError || !index || !mappings || !indexDocuments ? ( { - + diff --git a/x-pack/plugins/search_indices/public/components/quick_stats/quick_stats.tsx b/x-pack/plugins/search_indices/public/components/quick_stats/quick_stats.tsx index cece2b1d39910..32590cf3efa47 100644 --- a/x-pack/plugins/search_indices/public/components/quick_stats/quick_stats.tsx +++ b/x-pack/plugins/search_indices/public/components/quick_stats/quick_stats.tsx @@ -22,10 +22,12 @@ import { Mappings } from '../../types'; import { countVectorBasedTypesFromMappings } from './mappings_convertor'; import { QuickStat } from './quick_stat'; import { useKibana } from '../../hooks/use_kibana'; +import { IndexDocuments } from '../../hooks/api/use_document_search'; export interface QuickStatsProps { index: Index; mappings: Mappings; + indexDocuments: IndexDocuments; } export const SetupAISearchButton: React.FC = () => { @@ -60,12 +62,13 @@ export const SetupAISearchButton: React.FC = () => { ); }; -export const QuickStats: React.FC = ({ index, mappings }) => { +export const QuickStats: React.FC = ({ index, mappings, indexDocuments }) => { const [open, setOpen] = useState(false); const { euiTheme } = useEuiTheme(); const mappingStats = useMemo(() => countVectorBasedTypesFromMappings(mappings), [mappings]); const vectorFieldCount = mappingStats.sparse_vector + mappingStats.dense_vector + mappingStats.semantic_text; + const docCount = indexDocuments?.results._meta.page.total ?? 0; return ( = ({ index, mappings }) => { defaultMessage: 'Document count', })} data-test-subj="QuickStatsDocumentCount" - secondaryTitle={} + secondaryTitle={} stats={[ { title: i18n.translate('xpack.searchIndices.quickStats.documents.totalTitle', { defaultMessage: 'Total', }), - description: , + description: , }, { title: i18n.translate('xpack.searchIndices.quickStats.documents.indexSize', { diff --git a/x-pack/plugins/search_indices/public/hooks/api/use_delete_document.ts b/x-pack/plugins/search_indices/public/hooks/api/use_delete_document.ts index bbf43d684de56..4c5a64b270f91 100644 --- a/x-pack/plugins/search_indices/public/hooks/api/use_delete_document.ts +++ b/x-pack/plugins/search_indices/public/hooks/api/use_delete_document.ts @@ -40,12 +40,18 @@ export const useDeleteDocument = (indexName: string) => { queryClient.setQueryData( [QueryKeys.SearchDocuments, indexName], (snapshot: IndexDocuments | undefined) => { - const oldData = snapshot ?? { results: { data: [] } }; + const oldData = snapshot ?? { results: { data: [], _meta: { page: { total: 0 } } } }; return { ...oldData, results: { ...oldData.results, data: oldData.results.data.filter((doc: SearchHit) => doc._id !== id), + _meta: { + page: { + ...oldData.results._meta.page, + total: oldData.results._meta.page.total - 1, + }, + }, }, } as IndexDocuments; } diff --git a/x-pack/plugins/search_indices/public/hooks/api/use_document_search.ts b/x-pack/plugins/search_indices/public/hooks/api/use_document_search.ts index 7a74391809f60..d566b90916892 100644 --- a/x-pack/plugins/search_indices/public/hooks/api/use_document_search.ts +++ b/x-pack/plugins/search_indices/public/hooks/api/use_document_search.ts @@ -36,6 +36,7 @@ export const useIndexDocumentSearch = (indexName: string) => { http.post(`/internal/serverless_search/indices/${indexName}/search`, { body: JSON.stringify({ searchQuery: '', + trackTotalHits: true, }), query: { page: 0, diff --git a/x-pack/plugins/search_inference_endpoints/public/assets/images/providers/watsonx_ai.svg b/x-pack/plugins/search_inference_endpoints/public/assets/images/providers/watsonx_ai.svg new file mode 100644 index 0000000000000..29f7a735e6614 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/assets/images/providers/watsonx_ai.svg @@ -0,0 +1,3 @@ + + + diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/constants.ts b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/constants.ts index 3e60bc33b049c..26b6a93cec9fa 100644 --- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/constants.ts +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/constants.ts @@ -32,3 +32,10 @@ export const DEFAULT_INFERENCE_ENDPOINTS_TABLE_STATE: AllInferenceEndpointsTable filterOptions: DEFAULT_FILTER_OPTIONS, queryParams: DEFAULT_QUERY_PARAMS, }; + +export const PIPELINE_URL = 'ingest/ingest_pipelines'; + +export const PRECONFIGURED_ENDPOINTS = { + ELSER: '.elser-2', + E5: '.multi-e5-small', +}; diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/filter/multi_select_filter.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/filter/multi_select_filter.tsx index 84883c4e85432..790bb5ec09913 100644 --- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/filter/multi_select_filter.tsx +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/filter/multi_select_filter.tsx @@ -18,6 +18,7 @@ import { } from '@elastic/eui'; import { css } from '@emotion/react'; import React, { useState } from 'react'; +import _ from 'lodash'; import * as i18n from './translations'; export interface MultiSelectFilterOption { @@ -44,11 +45,14 @@ export const MultiSelectFilter: React.FC = ({ const { euiTheme } = useEuiTheme(); const [isPopoverOpen, setIsPopoverOpen] = useState(false); const toggleIsPopoverOpen = () => setIsPopoverOpen((prevValue) => !prevValue); - const options: MultiSelectFilterOption[] = rawOptions.map(({ key, label }) => ({ - label, - key, - checked: selectedOptionKeys.includes(key) ? 'on' : undefined, - })); + const options: MultiSelectFilterOption[] = _.uniqBy( + rawOptions.map(({ key, label }) => ({ + label, + key, + checked: selectedOptionKeys.includes(key) ? 'on' : undefined, + })), + 'label' + ); return ( diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/list_usage_results.test.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/list_usage_results.test.tsx index 40f821bc104ae..2907cc7ef8014 100644 --- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/list_usage_results.test.tsx +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/list_usage_results.test.tsx @@ -12,11 +12,11 @@ import { render, screen, fireEvent } from '@testing-library/react'; describe('ListUsageResults', () => { const items = [ { - label: 'index-1', + id: 'index-1', type: 'Index', }, { - label: 'pipeline-1', + id: 'pipeline-1', type: 'Pipeline', }, ]; diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/list_usage_results.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/list_usage_results.tsx index d20520345a8ba..d42b0f6735252 100644 --- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/list_usage_results.tsx +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/list_usage_results.tsx @@ -34,7 +34,7 @@ export const ListUsageResults: React.FC = ({ list }) => { {list - .filter((item) => item.label.toLowerCase().includes(term.toLowerCase())) + .filter((item) => item.id.toLowerCase().includes(term.toLowerCase())) .map((item, id) => ( ))} diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/scan_usage_results.test.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/scan_usage_results.test.tsx index d2ec41680d249..a9cb3f1f8d389 100644 --- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/scan_usage_results.test.tsx +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/scan_usage_results.test.tsx @@ -19,11 +19,11 @@ const mockOnCheckboxChange = jest.fn(); describe('ScanUsageResults', () => { const items = [ { - label: 'index-1', + id: 'index-1', type: 'Index', }, { - label: 'pipeline-1', + id: 'pipeline-1', type: 'Pipeline', }, ]; @@ -40,7 +40,7 @@ describe('ScanUsageResults', () => { ); }); @@ -58,9 +58,9 @@ describe('ScanUsageResults', () => { it('opens index management in a new tab', () => { fireEvent.click(screen.getByTestId('inferenceManagementOpenIndexManagement')); - expect(mockNavigateToApp).toHaveBeenCalledWith('enterprise_search', { + expect(mockNavigateToApp).toHaveBeenCalledWith('enterpriseSearchContent', { openInNewTab: true, - path: 'content/search_indices', + path: 'search_indices', }); }); diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/scan_usage_results.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/scan_usage_results.tsx index 0f4aa09c12be4..33d7a4dae891f 100644 --- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/scan_usage_results.tsx +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/scan_usage_results.tsx @@ -17,30 +17,31 @@ import { import React from 'react'; import { euiThemeVars } from '@kbn/ui-theme'; import { css } from '@emotion/react'; +import { ENTERPRISE_SEARCH_CONTENT_APP_ID } from '@kbn/deeplinks-search'; + import { InferenceUsageInfo } from '../../../../types'; import { useKibana } from '../../../../../../hooks/use_kibana'; import { RenderMessageWithIcon } from './render_message_with_icon'; - import * as i18n from '../delete/confirm_delete_endpoint/translations'; import { ListUsageResults } from './list_usage_results'; interface ScanUsageResultsProps { list: InferenceUsageInfo[]; ignoreWarningCheckbox: boolean; - onCheckboxChange: (state: boolean) => void; + onIgnoreWarningCheckboxChange: (state: boolean) => void; } export const ScanUsageResults: React.FC = ({ list, ignoreWarningCheckbox, - onCheckboxChange, + onIgnoreWarningCheckboxChange, }) => { const { services: { application }, } = useKibana(); - const handleNavigateToIndex = () => { - application?.navigateToApp('enterprise_search', { - path: 'content/search_indices', + const handleNavigateToIndexManagement = () => { + application?.navigateToApp(ENTERPRISE_SEARCH_CONTENT_APP_ID, { + path: 'search_indices', openInNewTab: true, }); }; @@ -59,7 +60,7 @@ export const ScanUsageResults: React.FC = ({ - + = ({ = ({ id={'ignoreWarningCheckbox'} label={i18n.IGNORE_POTENTIAL_ERRORS_LABEL} checked={ignoreWarningCheckbox} - onChange={(e) => onCheckboxChange(e.target.checked)} + onChange={(e) => onIgnoreWarningCheckboxChange(e.target.checked)} /> diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/usage_item.test.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/usage_item.test.tsx index 7315de521a1c3..6c6899c71922d 100644 --- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/usage_item.test.tsx +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/usage_item.test.tsx @@ -29,7 +29,7 @@ describe('UsageItem', () => { describe('index', () => { const item: InferenceUsageInfo = { - label: 'index-1', + id: 'index-1', type: 'Index', }; @@ -44,16 +44,16 @@ describe('UsageItem', () => { it('opens index in a new tab', () => { fireEvent.click(screen.getByRole('button')); - expect(mockNavigateToApp).toHaveBeenCalledWith('enterprise_search', { + expect(mockNavigateToApp).toHaveBeenCalledWith('enterpriseSearchContent', { openInNewTab: true, - path: 'content/search_indices/index-1', + path: 'search_indices/index-1', }); }); }); describe('pipeline', () => { const item: InferenceUsageInfo = { - label: 'pipeline-1', + id: 'pipeline-1', type: 'Pipeline', }; diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/usage_item.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/usage_item.tsx index 90bd050d67b81..577b9f8aa0e29 100644 --- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/usage_item.tsx +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/usage_item.tsx @@ -14,11 +14,15 @@ import { EuiText, EuiTextTruncate, EuiIcon, + EuiSpacer, } from '@elastic/eui'; import React from 'react'; +import { ENTERPRISE_SEARCH_CONTENT_APP_ID } from '@kbn/deeplinks-search'; +import { MANAGEMENT_APP_ID } from '@kbn/deeplinks-management/constants'; import { useKibana } from '../../../../../../hooks/use_kibana'; import { InferenceUsageInfo } from '../../../../types'; +import { PIPELINE_URL } from '../../../../constants'; interface UsageProps { usageItem: InferenceUsageInfo; @@ -29,27 +33,27 @@ export const UsageItem: React.FC = ({ usageItem }) => { } = useKibana(); const handleNavigateToIndex = () => { if (usageItem.type === 'Index') { - application?.navigateToApp('enterprise_search', { - path: `content/search_indices/${usageItem.label}`, + application?.navigateToApp(ENTERPRISE_SEARCH_CONTENT_APP_ID, { + path: `search_indices/${usageItem.id}`, openInNewTab: true, }); } else if (usageItem.type === 'Pipeline') { - application?.navigateToApp('management', { - path: `ingest/ingest_pipelines?pipeline=${usageItem.label}`, + application?.navigateToApp(MANAGEMENT_APP_ID, { + path: `${PIPELINE_URL}?pipeline=${usageItem.id}`, openInNewTab: true, }); } }; return ( - + - + @@ -58,7 +62,7 @@ export const UsageItem: React.FC = ({ usageItem }) => { - + @@ -66,6 +70,7 @@ export const UsageItem: React.FC = ({ usageItem }) => { + ); diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/index.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/index.tsx index 965f512b32d7d..06bd585c0eb28 100644 --- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/index.tsx +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/index.tsx @@ -37,7 +37,7 @@ export const ConfirmDeleteEndpointModal: React.FC { + const onIgnoreWarningCheckboxChange = (state: boolean) => { setIgnoreWarningCheckbox(state); if (state) { setDeleteDisabled(false); @@ -50,8 +50,11 @@ export const ConfirmDeleteEndpointModal: React.FC ({ label: index, type: 'Index' })); - const pipelines = data.pipelines.map((pipeline, id) => ({ label: pipeline, type: 'Pipeline' })); + const indices = data.indexes.map((index, id) => ({ id: index, type: 'Index' })); + const pipelines = data.pipelines.map((pipeline, id) => ({ + id: pipeline, + type: 'Pipeline', + })); const usages: InferenceUsageInfo[] = [...indices, ...pipelines]; if (usages.length > 0) { setDeleteDisabled(true); @@ -72,6 +75,7 @@ export const ConfirmDeleteEndpointModal: React.FC {i18n.CONFIRM_DELETE_WARNING} @@ -106,7 +110,7 @@ export const ConfirmDeleteEndpointModal: React.FC )} diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/translations.ts b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/translations.ts index d606e6f3c1b0e..b82fbcdf6b425 100644 --- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/translations.ts +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/translations.ts @@ -19,7 +19,7 @@ export const CONFIRM_DELETE_WARNING = i18n.translate( 'xpack.searchInferenceEndpoints.confirmDeleteEndpoint.confirmQuestion', { defaultMessage: - 'Deleting an inference endpoint currently in use will cause failures in the ingest and query attempts.', + 'Deleting an inference endpoint currently in use will cause failures in ingest and query attempts.', } ); @@ -54,7 +54,7 @@ export const POTENTIAL_FAILURE_LABEL = i18n.translate( export const IGNORE_POTENTIAL_ERRORS_LABEL = i18n.translate( 'xpack.searchInferenceEndpoints.confirmDeleteEndpoint.ignoreErrors', { - defaultMessage: 'Ignore potential errors and force deletion', + defaultMessage: 'Ignore errors and force deletion', } ); diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/delete_action.test.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/delete_action.test.tsx new file mode 100644 index 0000000000000..22c509ca22989 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/delete_action.test.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 { render, screen, fireEvent } from '@testing-library/react'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import React from 'react'; + +import { DeleteAction } from './delete_action'; +import { InferenceEndpointUI } from '../../../../types'; + +describe('Delete Action', () => { + const mockProvider = { + inference_id: 'my-hugging-face', + service: 'hugging_face', + service_settings: { + api_key: 'aaaa', + url: 'https://dummy.huggingface.com', + }, + task_settings: {}, + } as any; + + const mockItem: InferenceEndpointUI = { + endpoint: 'my-hugging-face', + provider: mockProvider, + type: 'text_embedding', + }; + + const Wrapper = ({ item }: { item: InferenceEndpointUI }) => { + const queryClient = new QueryClient(); + return ( + + + + ); + }; + it('renders', () => { + render(); + + expect(screen.getByTestId('inferenceUIDeleteAction')).toBeEnabled(); + }); + + it('disable the delete action for preconfigured endpoint', () => { + const preconfiguredMockItem = { ...mockItem, endpoint: '.elser-2' }; + render(); + + expect(screen.getByTestId('inferenceUIDeleteAction')).toBeDisabled(); + }); + + it('loads confirm delete modal', () => { + render(); + + fireEvent.click(screen.getByTestId('inferenceUIDeleteAction')); + expect(screen.getByTestId('deleteModalForInferenceUI')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/delete_action.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/delete_action.tsx index f932a3d25ed21..fb31aeb31dcaa 100644 --- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/delete_action.tsx +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/delete_action.tsx @@ -8,6 +8,7 @@ import { EuiButtonIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useState } from 'react'; +import { isEndpointPreconfigured } from '../../../../../../utils/preconfigured_endpoint_helper'; import { useDeleteEndpoint } from '../../../../../../hooks/use_delete_endpoint'; import { InferenceEndpointUI } from '../../../../types'; import { ConfirmDeleteEndpointModal } from './confirm_delete_endpoint'; @@ -39,6 +40,8 @@ export const DeleteAction: React.FC = ({ selectedEndpoint }) defaultMessage: 'Delete inference endpoint {selectedEndpointName}', values: { selectedEndpointName: selectedEndpoint.endpoint }, })} + data-test-subj="inferenceUIDeleteAction" + disabled={isEndpointPreconfigured(selectedEndpoint.endpoint)} key="delete" iconType="trash" color="danger" diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_endpoint/endpoint_info.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_endpoint/endpoint_info.tsx index 3d810a24a9ffc..26b12328dafd4 100644 --- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_endpoint/endpoint_info.tsx +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_endpoint/endpoint_info.tsx @@ -5,14 +5,28 @@ * 2.0. */ +import { EuiBetaBadge, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import React from 'react'; +import { isEndpointPreconfigured } from '../../../../utils/preconfigured_endpoint_helper'; +import * as i18n from './translations'; export interface EndpointInfoProps { inferenceId: string; } export const EndpointInfo: React.FC = ({ inferenceId }) => ( - - {inferenceId} - + + + + {inferenceId} + + + + + {isEndpointPreconfigured(inferenceId) ? ( + + ) : null} + + + ); diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_endpoint/translations.ts b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_endpoint/translations.ts new file mode 100644 index 0000000000000..70b1576a9ddc0 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_endpoint/translations.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 { i18n } from '@kbn/i18n'; + +export const PRECONFIGURED_LABEL = i18n.translate( + 'xpack.searchInferenceEndpoints.elasticsearch.endpointInfo.preconfigured', + { + defaultMessage: 'PRECONFIGURED', + } +); diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_service_provider/service_provider.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_service_provider/service_provider.tsx index 574b3881f121b..74f15f22762f1 100644 --- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_service_provider/service_provider.tsx +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_service_provider/service_provider.tsx @@ -21,6 +21,7 @@ import googleAIStudioIcon from '../../../../assets/images/providers/google_ai_st import mistralIcon from '../../../../assets/images/providers/mistral.svg'; import amazonBedrockIcon from '../../../../assets/images/providers/amazon_bedrock.svg'; import alibabaCloudAISearchIcon from '../../../../assets/images/providers/alibaba_cloud_ai_search.svg'; +import watsonxAIIcon from '../../../../assets/images/providers/watsonx_ai.svg'; import { ServiceProviderKeys } from '../../types'; import * as i18n from './translations'; @@ -78,6 +79,10 @@ export const SERVICE_PROVIDERS: Record = ({ providerEndpoint }) => { diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/search/table_search.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/search/table_search.tsx index b74e8050e5f92..b3989d60d9123 100644 --- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/search/table_search.tsx +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/search/table_search.tsx @@ -29,6 +29,7 @@ export const TableSearch: React.FC = ({ searchKey, se onChange={(e) => setSearchKey(e.target.value)} onSearch={onSearch} value={searchKey} + data-test-subj="search-field-endpoints" /> ); }; diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/tabular_page.test.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/tabular_page.test.tsx index cb23ba650e5fa..91cc303ed4568 100644 --- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/tabular_page.test.tsx +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/tabular_page.test.tsx @@ -45,6 +45,28 @@ const inferenceEndpoints = [ }, task_settings: {}, }, + { + inference_id: '.elser-2', + task_type: 'sparse_embedding', + service: 'elasticsearch', + service_settings: { + num_allocations: 1, + num_threads: 1, + model_id: '.elser_model_2', + }, + task_settings: {}, + }, + { + inference_id: '.multi-e5-small', + task_type: 'text_embedding', + service: 'elasticsearch', + service_settings: { + num_allocations: 1, + num_threads: 1, + model_id: '.multilingual-e5-small', + }, + task_settings: {}, + }, ] as InferenceAPIConfigResponse[]; jest.mock('../../hooks/use_delete_endpoint', () => ({ @@ -58,9 +80,11 @@ describe('When the tabular page is loaded', () => { render(); const rows = screen.getAllByRole('row'); - expect(rows[1]).toHaveTextContent('local-model'); - expect(rows[2]).toHaveTextContent('my-elser-model-05'); - expect(rows[3]).toHaveTextContent('third-party-model'); + expect(rows[1]).toHaveTextContent('.elser-2'); + expect(rows[2]).toHaveTextContent('.multi-e5-small'); + expect(rows[3]).toHaveTextContent('local-model'); + expect(rows[4]).toHaveTextContent('my-elser-model-05'); + expect(rows[5]).toHaveTextContent('third-party-model'); }); it('should display all service and model ids in the table', () => { @@ -68,12 +92,43 @@ describe('When the tabular page is loaded', () => { const rows = screen.getAllByRole('row'); expect(rows[1]).toHaveTextContent('Elasticsearch'); - expect(rows[1]).toHaveTextContent('.own_model'); + expect(rows[1]).toHaveTextContent('.elser_model_2'); expect(rows[2]).toHaveTextContent('Elasticsearch'); - expect(rows[2]).toHaveTextContent('.elser_model_2'); + expect(rows[2]).toHaveTextContent('.multilingual-e5-small'); - expect(rows[3]).toHaveTextContent('OpenAI'); + expect(rows[3]).toHaveTextContent('Elasticsearch'); expect(rows[3]).toHaveTextContent('.own_model'); + + expect(rows[4]).toHaveTextContent('Elasticsearch'); + expect(rows[4]).toHaveTextContent('.elser_model_2'); + + expect(rows[5]).toHaveTextContent('OpenAI'); + expect(rows[5]).toHaveTextContent('.own_model'); + }); + + it('should only disable delete action for preconfigured endpoints', () => { + render(); + + const deleteActions = screen.getAllByTestId('inferenceUIDeleteAction'); + + expect(deleteActions[0]).toBeDisabled(); + expect(deleteActions[1]).toBeDisabled(); + expect(deleteActions[2]).toBeEnabled(); + expect(deleteActions[3]).toBeEnabled(); + expect(deleteActions[4]).toBeEnabled(); + }); + + it('should show preconfigured badge only for preconfigured endpoints', () => { + render(); + + const preconfigured = 'PRECONFIGURED'; + + const rows = screen.getAllByRole('row'); + expect(rows[1]).toHaveTextContent(preconfigured); + expect(rows[2]).toHaveTextContent(preconfigured); + expect(rows[3]).not.toHaveTextContent(preconfigured); + expect(rows[4]).not.toHaveTextContent(preconfigured); + expect(rows[5]).not.toHaveTextContent(preconfigured); }); }); diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/tabular_page.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/tabular_page.tsx index 88a18a25b5a79..4cf4b3112396d 100644 --- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/tabular_page.tsx +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/tabular_page.tsx @@ -60,7 +60,6 @@ export const TabularPage: React.FC = ({ inferenceEndpoints }) return null; }, sortable: true, - truncateText: true, width: '300px', }, { diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/types.ts b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/types.ts index c1f23a3a4f2e3..0eec8a0cb177d 100644 --- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/types.ts +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/types.ts @@ -21,6 +21,7 @@ export enum ServiceProviderKeys { hugging_face = 'hugging_face', mistral = 'mistral', openai = 'openai', + watsonxai = 'watsonxai', } export enum SortFieldInferenceEndpoint { @@ -63,6 +64,6 @@ export interface InferenceEndpointUI { } export interface InferenceUsageInfo { - label: string; + id: string; type: string; } diff --git a/x-pack/plugins/search_inference_endpoints/public/components/inference_endpoints_header.tsx b/x-pack/plugins/search_inference_endpoints/public/components/inference_endpoints_header.tsx index f12ef3e9fe8cd..acb7e82db13b2 100644 --- a/x-pack/plugins/search_inference_endpoints/public/components/inference_endpoints_header.tsx +++ b/x-pack/plugins/search_inference_endpoints/public/components/inference_endpoints_header.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { EuiPageTemplate, EuiLink } from '@elastic/eui'; +import { EuiPageTemplate, EuiButtonEmpty } from '@elastic/eui'; import React from 'react'; import * as i18n from '../../common/translations'; import { docLinks } from '../../common/doc_links'; @@ -21,16 +21,28 @@ export const InferenceEndpointsHeader: React.FC = () => { description={i18n.MANAGE_INFERENCE_ENDPOINTS_LABEL} bottomBorder={true} rightSideItems={[ - {i18n.API_DOCUMENTATION_LINK} - , - + , + {i18n.VIEW_YOUR_MODELS_LINK} - , + , ]} /> ); diff --git a/x-pack/plugins/search_inference_endpoints/public/utils/preconfigured_endpoint_helper.test.ts b/x-pack/plugins/search_inference_endpoints/public/utils/preconfigured_endpoint_helper.test.ts new file mode 100644 index 0000000000000..b853109352dd9 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/utils/preconfigured_endpoint_helper.test.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { PRECONFIGURED_ENDPOINTS } from '../components/all_inference_endpoints/constants'; +import { isEndpointPreconfigured } from './preconfigured_endpoint_helper'; + +describe('Preconfigured Endpoint helper', () => { + it('return true for preconfigured elser', () => { + expect(isEndpointPreconfigured(PRECONFIGURED_ENDPOINTS.ELSER)).toEqual(true); + }); + + it('return true for preconfigured e5', () => { + expect(isEndpointPreconfigured(PRECONFIGURED_ENDPOINTS.E5)).toEqual(true); + }); + + it('return false for other endpoints', () => { + expect(isEndpointPreconfigured('other-endpoints')).toEqual(false); + }); +}); diff --git a/x-pack/plugins/search_inference_endpoints/public/utils/preconfigured_endpoint_helper.ts b/x-pack/plugins/search_inference_endpoints/public/utils/preconfigured_endpoint_helper.ts new file mode 100644 index 0000000000000..418e7e95319ef --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/utils/preconfigured_endpoint_helper.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { PRECONFIGURED_ENDPOINTS } from '../components/all_inference_endpoints/constants'; + +export const isEndpointPreconfigured = (endpoint: string) => + Object.values(PRECONFIGURED_ENDPOINTS).includes(endpoint); diff --git a/x-pack/plugins/search_inference_endpoints/tsconfig.json b/x-pack/plugins/search_inference_endpoints/tsconfig.json index 5b4a66e37d2f5..d454be99b65f0 100644 --- a/x-pack/plugins/search_inference_endpoints/tsconfig.json +++ b/x-pack/plugins/search_inference_endpoints/tsconfig.json @@ -31,7 +31,9 @@ "@kbn/test-jest-helpers", "@kbn/kibana-utils-plugin", "@kbn/features-plugin", - "@kbn/ui-theme" + "@kbn/ui-theme", + "@kbn/deeplinks-search", + "@kbn/deeplinks-management" ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/security/common/constants.ts b/x-pack/plugins/security/common/constants.ts index 3a9b20bbb0bd7..c75ab77a7de98 100644 --- a/x-pack/plugins/security/common/constants.ts +++ b/x-pack/plugins/security/common/constants.ts @@ -127,3 +127,14 @@ export const API_VERSIONS = { }, }, }; + +/** + * Privileges that define the superuser role or the role equivalent to the superuser role. + */ +export const SUPERUSER_PRIVILEGES = { + kibana: ['*'], + elasticsearch: { + cluster: ['all'], + index: { '*': ['all'] }, + }, +}; diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_table.tsx b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_table.tsx index 63124b4db2d44..f536b5838bab7 100644 --- a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_table.tsx +++ b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_table.tsx @@ -230,7 +230,7 @@ export const ApiKeysTable: FunctionComponent = ({ color: 'danger', onClick: (item) => onDelete([item]), available: deletable, - 'data-test-subj': 'apiKeysTableDeleteAction', + 'data-test-subj': (item) => `apiKeysTableDeleteAction-${item.name}`, }, ], }); diff --git a/x-pack/plugins/security/server/authorization/api_authorization.test.ts b/x-pack/plugins/security/server/authorization/api_authorization.test.ts index e928d73220274..0181c98d6f1b1 100644 --- a/x-pack/plugins/security/server/authorization/api_authorization.test.ts +++ b/x-pack/plugins/security/server/authorization/api_authorization.test.ts @@ -6,6 +6,7 @@ */ import type { RouteSecurity } from '@kbn/core/server'; +import { ReservedPrivilegesSet } from '@kbn/core/server'; import { coreMock, httpServerMock, @@ -149,7 +150,10 @@ describe('initAPIAuthorization', () => { asserts, }: { security?: RouteSecurity; - kibanaPrivilegesResponse?: Array<{ privilege: string; authorized: boolean }>; + kibanaPrivilegesResponse?: { + privileges: { kibana: Array<{ privilege: string; authorized: boolean }> }; + hasAllRequested?: boolean; + }; kibanaPrivilegesRequestActions?: string[]; asserts: { forbidden?: boolean; @@ -180,11 +184,7 @@ describe('initAPIAuthorization', () => { const mockResponse = httpServerMock.createResponseFactory(); const mockPostAuthToolkit = httpServiceMock.createOnPostAuthToolkit(); - const mockCheckPrivileges = jest.fn().mockReturnValue({ - privileges: { - kibana: kibanaPrivilegesResponse, - }, - }); + const mockCheckPrivileges = jest.fn().mockReturnValue(kibanaPrivilegesResponse); mockAuthz.mode.useRbacForRequest.mockReturnValue(true); mockAuthz.checkPrivilegesDynamicallyWithRequest.mockImplementation((request) => { // hapi conceals the actual "request" from us, so we make sure that the headers are passed to @@ -194,6 +194,12 @@ describe('initAPIAuthorization', () => { return mockCheckPrivileges; }); + mockAuthz.checkPrivilegesWithRequest.mockImplementation((request) => { + expect(request.headers).toMatchObject(headers); + + return { globally: () => kibanaPrivilegesResponse }; + }); + await postAuthHandler(mockRequest, mockResponse, mockPostAuthToolkit); expect(mockAuthz.mode.useRbacForRequest).toHaveBeenCalledWith(mockRequest); @@ -207,11 +213,13 @@ describe('initAPIAuthorization', () => { return; } - expect(mockCheckPrivileges).toHaveBeenCalledWith({ - kibana: kibanaPrivilegesRequestActions!.map((action: string) => - mockAuthz.actions.api.get(action) - ), - }); + if (kibanaPrivilegesRequestActions) { + expect(mockCheckPrivileges).toHaveBeenCalledWith({ + kibana: kibanaPrivilegesRequestActions!.map((action: string) => + mockAuthz.actions.api.get(action) + ), + }); + } if (asserts.forbidden) { expect(mockResponse.forbidden).toHaveBeenCalled(); @@ -239,11 +247,15 @@ describe('initAPIAuthorization', () => { ], }, }, - kibanaPrivilegesResponse: [ - { privilege: 'api:privilege1', authorized: true }, - { privilege: 'api:privilege2', authorized: true }, - { privilege: 'api:privilege3', authorized: false }, - ], + kibanaPrivilegesResponse: { + privileges: { + kibana: [ + { privilege: 'api:privilege1', authorized: true }, + { privilege: 'api:privilege2', authorized: true }, + { privilege: 'api:privilege3', authorized: false }, + ], + }, + }, kibanaPrivilegesRequestActions: ['privilege1', 'privilege2', 'privilege3'], asserts: { authzResult: { @@ -267,10 +279,14 @@ describe('initAPIAuthorization', () => { ], }, }, - kibanaPrivilegesResponse: [ - { privilege: 'api:privilege1', authorized: true }, - { privilege: 'api:privilege2', authorized: true }, - ], + kibanaPrivilegesResponse: { + privileges: { + kibana: [ + { privilege: 'api:privilege1', authorized: true }, + { privilege: 'api:privilege2', authorized: true }, + ], + }, + }, kibanaPrivilegesRequestActions: ['privilege1', 'privilege2'], asserts: { authzResult: { @@ -293,11 +309,15 @@ describe('initAPIAuthorization', () => { ], }, }, - kibanaPrivilegesResponse: [ - { privilege: 'api:privilege1', authorized: false }, - { privilege: 'api:privilege2', authorized: true }, - { privilege: 'api:privilege3', authorized: false }, - ], + kibanaPrivilegesResponse: { + privileges: { + kibana: [ + { privilege: 'api:privilege1', authorized: false }, + { privilege: 'api:privilege2', authorized: true }, + { privilege: 'api:privilege3', authorized: false }, + ], + }, + }, kibanaPrivilegesRequestActions: ['privilege1', 'privilege2', 'privilege3'], asserts: { authzResult: { @@ -317,10 +337,14 @@ describe('initAPIAuthorization', () => { requiredPrivileges: ['privilege1', 'privilege2'], }, }, - kibanaPrivilegesResponse: [ - { privilege: 'api:privilege1', authorized: true }, - { privilege: 'api:privilege2', authorized: true }, - ], + kibanaPrivilegesResponse: { + privileges: { + kibana: [ + { privilege: 'api:privilege1', authorized: true }, + { privilege: 'api:privilege2', authorized: true }, + ], + }, + }, kibanaPrivilegesRequestActions: ['privilege1', 'privilege2'], asserts: { authzResult: { @@ -344,18 +368,54 @@ describe('initAPIAuthorization', () => { ], }, }, - kibanaPrivilegesResponse: [ - { privilege: 'api:privilege1', authorized: true }, - { privilege: 'api:privilege2', authorized: false }, - { privilege: 'api:privilege3', authorized: false }, - ], kibanaPrivilegesRequestActions: ['privilege1', 'privilege2', 'privilege3'], + kibanaPrivilegesResponse: { + privileges: { + kibana: [ + { privilege: 'api:privilege1', authorized: true }, + { privilege: 'api:privilege2', authorized: false }, + { privilege: 'api:privilege3', authorized: false }, + ], + }, + }, asserts: { forbidden: true, }, } ); + testSecurityConfig( + `protected route restricted to only superusers returns forbidden if user not a superuser`, + { + security: { + authz: { + requiredPrivileges: [ReservedPrivilegesSet.superuser], + }, + }, + kibanaPrivilegesResponse: { privileges: { kibana: [] }, hasAllRequested: false }, + asserts: { + forbidden: true, + }, + } + ); + + testSecurityConfig( + `protected route allowed only for superuser access returns "authzResult" if user is superuser`, + { + security: { + authz: { + requiredPrivileges: [ReservedPrivilegesSet.superuser], + }, + }, + kibanaPrivilegesResponse: { privileges: { kibana: [] }, hasAllRequested: true }, + asserts: { + authzResult: { + [ReservedPrivilegesSet.superuser]: true, + }, + }, + } + ); + testSecurityConfig( `protected route returns forbidden if user doesn't have at least one from allRequired privileges requested`, { @@ -369,12 +429,16 @@ describe('initAPIAuthorization', () => { ], }, }, - kibanaPrivilegesResponse: [ - { privilege: 'api:privilege1', authorized: true }, - { privilege: 'api:privilege2', authorized: false }, - { privilege: 'api:privilege3', authorized: false }, - { privilege: 'api:privilege4', authorized: true }, - ], + kibanaPrivilegesResponse: { + privileges: { + kibana: [ + { privilege: 'api:privilege1', authorized: true }, + { privilege: 'api:privilege2', authorized: false }, + { privilege: 'api:privilege3', authorized: false }, + { privilege: 'api:privilege4', authorized: true }, + ], + }, + }, kibanaPrivilegesRequestActions: ['privilege1', 'privilege2', 'privilege3', 'privilege4'], asserts: { forbidden: true, @@ -390,10 +454,14 @@ describe('initAPIAuthorization', () => { requiredPrivileges: ['privilege1', 'privilege2'], }, }, - kibanaPrivilegesResponse: [ - { privilege: 'api:privilege1', authorized: true }, - { privilege: 'api:privilege2', authorized: false }, - ], + kibanaPrivilegesResponse: { + privileges: { + kibana: [ + { privilege: 'api:privilege1', authorized: true }, + { privilege: 'api:privilege2', authorized: false }, + ], + }, + }, kibanaPrivilegesRequestActions: ['privilege1', 'privilege2'], asserts: { forbidden: true, diff --git a/x-pack/plugins/security/server/authorization/api_authorization.ts b/x-pack/plugins/security/server/authorization/api_authorization.ts index 9c67ff8bdff8b..2b99c2a176ac1 100644 --- a/x-pack/plugins/security/server/authorization/api_authorization.ts +++ b/x-pack/plugins/security/server/authorization/api_authorization.ts @@ -14,18 +14,28 @@ import type { PrivilegeSet, RouteAuthz, } from '@kbn/core/server'; +import { ReservedPrivilegesSet } from '@kbn/core/server'; import type { AuthorizationServiceSetup } from '@kbn/security-plugin-types-server'; import type { RecursiveReadonly } from '@kbn/utility-types'; -import { API_OPERATION_PREFIX } from '../../common/constants'; +import { API_OPERATION_PREFIX, SUPERUSER_PRIVILEGES } from '../../common/constants'; const isAuthzDisabled = (authz?: RecursiveReadonly): authz is AuthzDisabled => { return (authz as AuthzDisabled)?.enabled === false; }; +const isReservedPrivilegeSet = (privilege: string): privilege is ReservedPrivilegesSet => { + return Object.hasOwn(ReservedPrivilegesSet, privilege); +}; + export function initAPIAuthorization( http: HttpServiceSetup, - { actions, checkPrivilegesDynamicallyWithRequest, mode }: AuthorizationServiceSetup, + { + actions, + checkPrivilegesDynamicallyWithRequest, + checkPrivilegesWithRequest, + mode, + }: AuthorizationServiceSetup, logger: Logger ) { http.registerOnPostAuth(async (request, response, toolkit) => { @@ -47,24 +57,54 @@ export function initAPIAuthorization( const authz = security.authz as AuthzEnabled; - const requestedPrivileges = authz.requiredPrivileges.flatMap((privilegeEntry) => { - if (typeof privilegeEntry === 'object') { - return [...(privilegeEntry.allRequired ?? []), ...(privilegeEntry.anyRequired ?? [])]; + const { requestedPrivileges, requestedReservedPrivileges } = authz.requiredPrivileges.reduce( + (acc, privilegeEntry) => { + const privileges = + typeof privilegeEntry === 'object' + ? [...(privilegeEntry.allRequired ?? []), ...(privilegeEntry.anyRequired ?? [])] + : [privilegeEntry]; + + for (const privilege of privileges) { + if (isReservedPrivilegeSet(privilege)) { + acc.requestedReservedPrivileges.push(privilege); + } else { + acc.requestedPrivileges.push(privilege); + } + } + + return acc; + }, + { + requestedPrivileges: [] as string[], + requestedReservedPrivileges: [] as string[], } + ); - return privilegeEntry; - }); - - const apiActions = requestedPrivileges.map((permission) => actions.api.get(permission)); const checkPrivileges = checkPrivilegesDynamicallyWithRequest(request); - const checkPrivilegesResponse = await checkPrivileges({ kibana: apiActions }); - const privilegeToApiOperation = (privilege: string) => privilege.replace(API_OPERATION_PREFIX, ''); + const kibanaPrivileges: Record = {}; - for (const kbPrivilege of checkPrivilegesResponse.privileges.kibana) { - kibanaPrivileges[privilegeToApiOperation(kbPrivilege.privilege)] = kbPrivilege.authorized; + if (requestedPrivileges.length > 0) { + const checkPrivilegesResponse = await checkPrivileges({ + kibana: requestedPrivileges.map((permission) => actions.api.get(permission)), + }); + + for (const kbPrivilege of checkPrivilegesResponse.privileges.kibana) { + kibanaPrivileges[privilegeToApiOperation(kbPrivilege.privilege)] = kbPrivilege.authorized; + } + } + + for (const reservedPrivilege of requestedReservedPrivileges) { + if (reservedPrivilege === ReservedPrivilegesSet.superuser) { + const checkSuperuserPrivilegesResponse = await checkPrivilegesWithRequest( + request + ).globally(SUPERUSER_PRIVILEGES); + + kibanaPrivileges[ReservedPrivilegesSet.superuser] = + checkSuperuserPrivilegesResponse.hasAllRequested; + } } const hasRequestedPrivilege = (kbPrivilege: Privilege | PrivilegeSet) => { diff --git a/x-pack/plugins/security/server/config.test.ts b/x-pack/plugins/security/server/config.test.ts index f5a735fdfe8b7..2e2199ff850a1 100644 --- a/x-pack/plugins/security/server/config.test.ts +++ b/x-pack/plugins/security/server/config.test.ts @@ -247,7 +247,7 @@ describe('config schema', () => { }, "loginAssistanceMessage": "", "public": Object {}, - "roleManagementEnabled": false, + "roleManagementEnabled": true, "secureCookies": false, "session": Object { "cleanupInterval": "PT1H", diff --git a/x-pack/plugins/security/server/config.ts b/x-pack/plugins/security/server/config.ts index 5618186459566..8be1500bdccf1 100644 --- a/x-pack/plugins/security/server/config.ts +++ b/x-pack/plugins/security/server/config.ts @@ -303,8 +303,9 @@ export const ConfigSchema = schema.object({ ), }), + // config/serverless.oblt.yml contains an override to false for OBLT projects roleManagementEnabled: offeringBasedSchema({ - serverless: schema.boolean({ defaultValue: false }), + serverless: schema.boolean({ defaultValue: true }), }), // Setting only allowed in the Serverless offering diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/entities/common.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/entities/common.gen.ts index 7359d36c9cbfa..77607a6ceb863 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/entities/common.gen.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/entities/common.gen.ts @@ -24,7 +24,7 @@ export const UserEntity = z.object({ '@timestamp': z.string().datetime(), entity: z.object({ name: z.string(), - source: z.array(z.string()), + source: z.string(), }), user: z.object({ full_name: z.array(z.string()).optional(), @@ -48,7 +48,7 @@ export const HostEntity = z.object({ '@timestamp': z.string().datetime(), entity: z.object({ name: z.string(), - source: z.array(z.string()), + source: z.string(), }), host: z.object({ hostname: z.array(z.string()).optional(), diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/entities/common.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/entities/common.schema.yaml index 35314dfed9f54..045a04ff4867a 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/entities/common.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/entities/common.schema.yaml @@ -22,12 +22,10 @@ components: - name - source properties: - name: + name: type: string source: - type: array - items: - type: string + type: string user: type: object properties: @@ -84,12 +82,10 @@ components: - name - source properties: - name: + name: type: string source: - type: array - items: - type: string + type: string host: type: object properties: diff --git a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml index d3cce9170ae6a..fef2850b44c90 100644 --- a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml +++ b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml @@ -925,9 +925,7 @@ components: name: type: string source: - items: - type: string - type: array + type: string required: - name - source @@ -1077,9 +1075,7 @@ components: name: type: string source: - items: - type: string - type: array + type: string required: - name - source diff --git a/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml index eecca3fe07ae6..3a5054f17a460 100644 --- a/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml +++ b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml @@ -925,9 +925,7 @@ components: name: type: string source: - items: - type: string - type: array + type: string required: - name - source @@ -1077,9 +1075,7 @@ components: name: type: string source: - items: - type: string - type: array + type: string required: - name - source diff --git a/x-pack/plugins/security_solution/public/app/solution_navigation/links/sections/investigations_translations.ts b/x-pack/plugins/security_solution/public/app/solution_navigation/links/sections/investigations_translations.ts index d70717783870a..931c3c20d4002 100644 --- a/x-pack/plugins/security_solution/public/app/solution_navigation/links/sections/investigations_translations.ts +++ b/x-pack/plugins/security_solution/public/app/solution_navigation/links/sections/investigations_translations.ts @@ -24,7 +24,8 @@ export const TIMELINE_DESCRIPTION = i18n.translate( export const NOTE_DESCRIPTION = i18n.translate( 'xpack.securitySolution.navLinks.investigations.note.title', { - defaultMessage: 'Oversee, revise and revisit the annotations within each document and timeline', + defaultMessage: + 'Oversee, revise, and revisit the notes attached to alerts, events and Timelines.', } ); diff --git a/x-pack/plugins/security_solution/public/common/components/header_actions/add_note_icon_item.test.tsx b/x-pack/plugins/security_solution/public/common/components/header_actions/add_note_icon_item.test.tsx index 98dfc83e9d3e8..b07aa7aedfcad 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_actions/add_note_icon_item.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_actions/add_note_icon_item.test.tsx @@ -37,7 +37,7 @@ const renderTestComponent = (props: Partial = { timelineType: TimelineTypeEnum.default, eventId: 'event-1', - ariaLabel: 'Add Note', + ariaLabel: 'Add note', toggleShowNotes: toggleShowNotesMock, notesCount: 2, ...props, @@ -76,12 +76,12 @@ describe('AddEventNoteAction', () => { expect(NotesButtonMock).toHaveBeenCalledWith( expect.objectContaining({ - ariaLabel: 'Add Note', + ariaLabel: 'Add note', 'data-test-subj': 'add-note', isDisabled: false, timelineType: TimelineTypeEnum.default, toggleShowNotes: expect.any(Function), - toolTip: '2 Notes available. Click to view them & add more.', + toolTip: '2 notes available. Click to view them and add more.', eventId: 'event-1', notesCount: 2, }), @@ -98,12 +98,12 @@ describe('AddEventNoteAction', () => { expect(NotesButtonMock).toHaveBeenCalledWith( expect.objectContaining({ - ariaLabel: 'Add Note', + ariaLabel: 'Add note', 'data-test-subj': 'add-note', isDisabled: false, timelineType: TimelineTypeEnum.default, toggleShowNotes: expect.any(Function), - toolTip: '1 Note available. Click to view it & add more.', + toolTip: '1 note available. Click to view it and add more.', eventId: 'event-2', notesCount: 1, }), @@ -120,12 +120,12 @@ describe('AddEventNoteAction', () => { expect(NotesButtonMock).toHaveBeenCalledWith( expect.objectContaining({ - ariaLabel: 'Add Note', + ariaLabel: 'Add note', 'data-test-subj': 'add-note', isDisabled: false, timelineType: TimelineTypeEnum.default, toggleShowNotes: expect.any(Function), - toolTip: 'Add Note', + toolTip: 'Add note', eventId: 'event-3', notesCount: 0, }), diff --git a/x-pack/plugins/security_solution/public/common/components/header_actions/add_note_icon_item.tsx b/x-pack/plugins/security_solution/public/common/components/header_actions/add_note_icon_item.tsx index f9539b9062331..f931042863a62 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_actions/add_note_icon_item.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_actions/add_note_icon_item.tsx @@ -6,12 +6,34 @@ */ import React, { useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; import { NotesButton } from '../../../timelines/components/timeline/properties/helpers'; import { type TimelineType, TimelineTypeEnum } from '../../../../common/api/timeline'; import { useUserPrivileges } from '../user_privileges'; -import * as i18n from './translations'; import { ActionIconItem } from './action_icon_item'; +const NOTES_DISABLE_TOOLTIP = i18n.translate( + 'xpack.securitySolution.timeline.body.notes.disableEventTooltip', + { + defaultMessage: 'Notes cannot be added here while editing a template Timeline.', + } +); +const NOTES_ADD_TOOLTIP = i18n.translate( + 'xpack.securitySolution.timeline.body.notes.addNoteTooltip', + { + defaultMessage: 'Add note', + } +); +const NOTES_COUNT_TOOLTIP = ({ notesCount }: { notesCount: number }) => + i18n.translate( + 'xpack.securitySolution.timeline.body.notes.addNote.multipleNotesAvailableTooltip', + { + values: { notesCount }, + defaultMessage: + '{notesCount} {notesCount, plural, one {note} other {notes} } available. Click to view {notesCount, plural, one {it} other {them}} and add more.', + } + ); + interface AddEventNoteActionProps { ariaLabel?: string; timelineType: TimelineType; @@ -33,7 +55,7 @@ const AddEventNoteActionComponent: React.FC = ({ const { kibanaSecuritySolutionsPrivileges } = useUserPrivileges(); const NOTES_TOOLTIP = useMemo( - () => (notesCount > 0 ? i18n.NOTES_COUNT_TOOLTIP({ notesCount }) : i18n.NOTES_ADD_TOOLTIP), + () => (notesCount > 0 ? NOTES_COUNT_TOOLTIP({ notesCount }) : NOTES_ADD_TOOLTIP), [notesCount] ); @@ -45,9 +67,7 @@ const AddEventNoteActionComponent: React.FC = ({ isDisabled={kibanaSecuritySolutionsPrivileges.crud === false} timelineType={timelineType} toggleShowNotes={toggleShowNotes} - toolTip={ - timelineType === TimelineTypeEnum.template ? i18n.NOTES_DISABLE_TOOLTIP : NOTES_TOOLTIP - } + toolTip={timelineType === TimelineTypeEnum.template ? NOTES_DISABLE_TOOLTIP : NOTES_TOOLTIP} eventId={eventId} notesCount={notesCount} /> diff --git a/x-pack/plugins/security_solution/public/common/components/header_actions/translations.ts b/x-pack/plugins/security_solution/public/common/components/header_actions/translations.ts index 10832ccfac1e5..03ff7503643c9 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_actions/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/header_actions/translations.ts @@ -14,30 +14,6 @@ export const OPEN_SESSION_VIEW = i18n.translate( } ); -export const NOTES_DISABLE_TOOLTIP = i18n.translate( - 'xpack.securitySolution.timeline.body.notes.disableEventTooltip', - { - defaultMessage: 'Notes may not be added here while editing a template timeline', - } -); - -export const NOTES_ADD_TOOLTIP = i18n.translate( - 'xpack.securitySolution.timeline.body.notes.addNoteTooltip', - { - defaultMessage: 'Add Note', - } -); - -export const NOTES_COUNT_TOOLTIP = ({ notesCount }: { notesCount: number }) => - i18n.translate( - 'xpack.securitySolution.timeline.body.notes.addNote.multipleNotesAvailableTooltip', - { - values: { notesCount }, - defaultMessage: - '{notesCount} {notesCount, plural, one {Note} other {Notes} } available. Click to view {notesCount, plural, one {it} other {them}} & add more.', - } - ); - export const SORT_FIELDS = i18n.translate('xpack.securitySolution.timeline.sortFieldsButton', { defaultMessage: 'Sort fields', }); diff --git a/x-pack/plugins/security_solution/public/common/components/import_data_modal/index.tsx b/x-pack/plugins/security_solution/public/common/components/import_data_modal/index.tsx index 7fee04747408e..0d4e51692e7c9 100644 --- a/x-pack/plugins/security_solution/public/common/components/import_data_modal/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/import_data_modal/index.tsx @@ -91,6 +91,7 @@ export const ImportDataModalComponent = ({ setOverwriteExceptions(false); setOverwriteActionConnectors(false); setActionConnectorsWarnings([]); + setIsImporting(false); }, [closeModal, setOverwrite, setOverwriteExceptions]); const onImportComplete = useCallback( diff --git a/x-pack/plugins/security_solution/public/common/components/user_profiles/use_suggest_users.tsx b/x-pack/plugins/security_solution/public/common/components/user_profiles/use_suggest_users.tsx index a8a2338e51e9d..626d621f61a30 100644 --- a/x-pack/plugins/security_solution/public/common/components/user_profiles/use_suggest_users.tsx +++ b/x-pack/plugins/security_solution/public/common/components/user_profiles/use_suggest_users.tsx @@ -13,10 +13,6 @@ import { suggestUsers } from './api'; import { USER_PROFILES_FAILURE } from './translations'; import { useAppToasts } from '../../hooks/use_app_toasts'; -export interface SuggestUserProfilesArgs { - searchTerm: string; -} - export const bulkGetUserProfiles = async ({ searchTerm, }: { @@ -25,7 +21,21 @@ export const bulkGetUserProfiles = async ({ return suggestUsers({ searchTerm }); }; -export const useSuggestUsers = ({ searchTerm }: { searchTerm: string }) => { +export interface UseSuggestUsersParams { + /** + * Search term to filter user profiles + */ + searchTerm: string; + /** + * Whether the query should be enabled + */ + enabled?: boolean; +} + +/** + * Fetches user profiles based on a search term + */ +export const useSuggestUsers = ({ enabled = true, searchTerm }: UseSuggestUsersParams) => { const { addError } = useAppToasts(); return useQuery( @@ -36,6 +46,7 @@ export const useSuggestUsers = ({ searchTerm }: { searchTerm: string }) => { { retry: false, staleTime: Infinity, + enabled, onError: (e) => { addError(e, { title: USER_PROFILES_FAILURE }); }, diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/constants.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/constants.ts index 5126d75178f5f..cb247891d79b3 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/constants.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/constants.ts @@ -35,6 +35,8 @@ export enum TELEMETRY_EVENT { DASHBOARD = 'navigate_to_dashboard', CREATE_DASHBOARD = 'create_dashboard', + ONBOARDING = 'onboarding', + // value list OPEN_VALUE_LIST_MODAL = 'open_value_list_modal', CREATE_VALUE_LIST_ITEM = 'create_value_list_item', diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.test.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.test.ts index d10bb4bb03e08..bc7c288906e8e 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.test.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.test.ts @@ -854,9 +854,7 @@ describe('Detections Rules API', () => { expect(fetchMock).toHaveBeenCalledWith( expect.any(String), expect.objectContaining({ - query: expect.objectContaining({ - filter: 'alert.id:"alert:id1" or alert.id:"alert:id2"', - }), + body: '{"filter":"alert.id:\\"alert:id1\\" or alert.id:\\"alert:id2\\"","fields":"[\\"muteAll\\",\\"activeSnoozes\\",\\"isSnoozedUntil\\",\\"snoozeSchedule\\"]","per_page":2}', }) ); }); @@ -867,9 +865,7 @@ describe('Detections Rules API', () => { expect(fetchMock).toHaveBeenCalledWith( expect.any(String), expect.objectContaining({ - query: expect.objectContaining({ - per_page: 2, - }), + body: '{"filter":"alert.id:\\"alert:id1\\" or alert.id:\\"alert:id2\\"","fields":"[\\"muteAll\\",\\"activeSnoozes\\",\\"isSnoozedUntil\\",\\"snoozeSchedule\\"]","per_page":2}', }) ); }); @@ -880,14 +876,7 @@ describe('Detections Rules API', () => { expect(fetchMock).toHaveBeenCalledWith( expect.any(String), expect.objectContaining({ - query: expect.objectContaining({ - fields: JSON.stringify([ - 'muteAll', - 'activeSnoozes', - 'isSnoozedUntil', - 'snoozeSchedule', - ]), - }), + body: '{"filter":"alert.id:\\"alert:id1\\" or alert.id:\\"alert:id2\\"","fields":"[\\"muteAll\\",\\"activeSnoozes\\",\\"isSnoozedUntil\\",\\"snoozeSchedule\\"]","per_page":2}', }) ); }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.ts index c86606d0d8137..1e2ee1be7a47a 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.ts @@ -241,12 +241,12 @@ export const fetchRulesSnoozeSettings = async ({ const response = await KibanaServices.get().http.fetch( INTERNAL_ALERTING_API_FIND_RULES_PATH, { - method: 'GET', - query: { + method: 'POST', + body: JSON.stringify({ filter: ids.map((x) => `alert.id:"alert:${x}"`).join(' or '), fields: JSON.stringify(['muteAll', 'activeSnoozes', 'isSnoozedUntil', 'snoozeSchedule']), per_page: ids.length, - }, + }), signal, } ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/use_fetch_rules_snooze_settings_query.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/use_fetch_rules_snooze_settings_query.ts index 8d2ca14d79f9b..a0eedb35d4fa1 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/use_fetch_rules_snooze_settings_query.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/use_fetch_rules_snooze_settings_query.ts @@ -13,7 +13,7 @@ import type { RulesSnoozeSettingsMap } from '../../logic'; import { fetchRulesSnoozeSettings } from '../api'; import { DEFAULT_QUERY_OPTIONS } from './constants'; -const FETCH_RULE_SNOOZE_SETTINGS_QUERY_KEY = ['GET', INTERNAL_ALERTING_API_FIND_RULES_PATH]; +const FETCH_RULE_SNOOZE_SETTINGS_QUERY_KEY = ['POST', INTERNAL_ALERTING_API_FIND_RULES_PATH]; /** * A wrapper around useQuery provides default values to the underlying query, diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/rule_management/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/rule_management/index.tsx index 7033785b20052..17c5368a4b1c5 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/rule_management/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/rule_management/index.tsx @@ -35,7 +35,6 @@ import { AllRules } from '../../components/rules_table'; import { RulesTableContextProvider } from '../../components/rules_table/rules_table/rules_table_context'; import { useInvalidateFetchCoverageOverviewQuery } from '../../../rule_management/api/hooks/use_fetch_coverage_overview_query'; import { HeaderPage } from '../../../../common/components/header_page'; -import { RuleFeatureTour } from '../../components/rules_table/feature_tour/rules_feature_tour'; const RulesPageComponent: React.FC = () => { const [isImportModalVisible, showImportModal, hideImportModal] = useBoolState(); @@ -173,7 +172,6 @@ const RulesPageComponent: React.FC = () => { kibanaServices={kibanaServices} categories={[DEFAULT_APP_CATEGORIES.security.id]} /> - diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/components/entity_source_filter.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/components/entity_source_filter.tsx index aac8aad170f3f..bc295b6cde439 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/components/entity_source_filter.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/components/entity_source_filter.tsx @@ -8,30 +8,26 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { MultiselectFilter } from '../../../../common/components/multiselect_filter'; +import { EntitySourceTag } from '../types'; interface SourceFilterProps { - selectedItems: EntitySource[]; - onChange: (selectedItems: EntitySource[]) => void; + selectedItems: EntitySourceTag[]; + onChange: (selectedItems: EntitySourceTag[]) => void; } -export enum EntitySource { - CSV_UPLOAD = 'CSV upload', - EVENTS = 'Events', -} -// TODO Fix the Entity Source field before using it export const EntitySourceFilter: React.FC = ({ selectedItems, onChange }) => { return ( - + title={i18n.translate( 'xpack.securitySolution.entityAnalytics.entityStore.entitySource.filterTitle', { defaultMessage: 'Source', } )} - items={Object.values(EntitySource)} + items={Object.values(EntitySourceTag)} selectedItems={selectedItems} onSelectionChange={onChange} - width={140} + width={190} /> ); }; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/entities_list.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/entities_list.test.tsx index b105a87fd8720..0e598d6463c5a 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/entities_list.test.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/entities_list.test.tsx @@ -33,7 +33,7 @@ const responseData: ListEntitiesResponse = { user: { name: entityName }, entity: { name: entityName, - source: ['source'], + source: 'test-index', }, }, ], @@ -106,7 +106,7 @@ describe('EntitiesList', () => { fireEvent.click(columnHeader); expect(mockUseEntitiesListQuery).toHaveBeenCalledWith( expect.objectContaining({ - sortField: 'entity.displayName.keyword', + sortField: 'entity.name.text', sortOrder: 'asc', }) ); diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/entities_list.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/entities_list.tsx index a6e058af34392..fc821bead61f0 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/entities_list.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/entities_list.tsx @@ -21,12 +21,13 @@ import { EntityType } from '../../../../common/api/entity_analytics/entity_store import type { Criteria } from '../../../explore/components/paginated_table'; import { PaginatedTable } from '../../../explore/components/paginated_table'; import { SeverityFilter } from '../severity/severity_filter'; -import type { EntitySource } from './components/entity_source_filter'; +import { EntitySourceFilter } from './components/entity_source_filter'; import { useEntitiesListFilters } from './hooks/use_entities_list_filters'; import { AssetCriticalityFilter } from '../asset_criticality/asset_criticality_filter'; import { useEntitiesListQuery } from './hooks/use_entities_list_query'; import { ENTITIES_LIST_TABLE_ID, rowItems } from './constants'; import { useEntitiesListColumns } from './hooks/use_entities_list_columns'; +import type { EntitySourceTag } from './types'; export const EntitiesList: React.FC = () => { const { deleteQuery, setQuery, isInitializing, from, to } = useGlobalTime(); @@ -40,7 +41,7 @@ export const EntitiesList: React.FC = () => { const [selectedSeverities, setSelectedSeverities] = useState([]); const [selectedCriticalities, setSelectedCriticalities] = useState([]); - const [selectedSources, _] = useState([]); + const [selectedSources, setSelectedSources] = useState([]); const filter = useEntitiesListFilters({ selectedSeverities, @@ -147,6 +148,7 @@ export const EntitiesList: React.FC = () => { selectedItems={selectedCriticalities} onChange={setSelectedCriticalities} /> + diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/helpers.test.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/helpers.test.ts index 6e3ba0e6d09d1..55fb85bf9158b 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/helpers.test.ts @@ -5,40 +5,70 @@ * 2.0. */ -import { isUserEntity } from './helpers'; +import { isUserEntity, sourceFieldToText } from './helpers'; import type { Entity, UserEntity, } from '../../../../common/api/entity_analytics/entity_store/entities/common.gen'; +import { render } from '@testing-library/react'; +import { TestProviders } from '@kbn/timelines-plugin/public/mock'; -describe('isUserEntity', () => { - it('should return true if the record is a UserEntity', () => { - const userEntity: UserEntity = { - '@timestamp': '2021-08-02T14:00:00.000Z', - user: { - name: 'test_user', - }, - entity: { - name: 'test_user', - source: ['logs-test'], - }, - }; - - expect(isUserEntity(userEntity)).toBe(true); +describe('helpers', () => { + describe('isUserEntity', () => { + it('should return true if the record is a UserEntity', () => { + const userEntity: UserEntity = { + '@timestamp': '2021-08-02T14:00:00.000Z', + user: { + name: 'test_user', + }, + entity: { + name: 'test_user', + source: 'logs-test', + }, + }; + + expect(isUserEntity(userEntity)).toBe(true); + }); + + it('should return false if the record is not a UserEntity', () => { + const nonUserEntity: Entity = { + '@timestamp': '2021-08-02T14:00:00.000Z', + host: { + name: 'test_host', + }, + entity: { + name: 'test_host', + source: 'logs-test', + }, + }; + + expect(isUserEntity(nonUserEntity)).toBe(false); + }); }); - it('should return false if the record is not a UserEntity', () => { - const nonUserEntity: Entity = { - '@timestamp': '2021-08-02T14:00:00.000Z', - host: { - name: 'test_host', - }, - entity: { - name: 'test_host', - source: ['logs-test'], - }, - }; - - expect(isUserEntity(nonUserEntity)).toBe(false); + describe('sourceFieldToText', () => { + it("should return 'Events' if the value isn't risk or asset", () => { + const { container } = render(sourceFieldToText('anything'), { + wrapper: TestProviders, + }); + + expect(container).toHaveTextContent('Events'); + }); + + it("should return 'Risk' if the value is a risk index", () => { + const { container } = render(sourceFieldToText('risk-score.risk-score-default'), { + wrapper: TestProviders, + }); + + expect(container).toHaveTextContent('Risk'); + }); + + it("should return 'Asset Criticality' if the value is a asset criticality index", () => { + const { container } = render(sourceFieldToText('.asset-criticality.asset-criticality-*'), { + wrapper: TestProviders, + }); + + expect(container).toHaveTextContent('Asset Criticality'); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/helpers.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/helpers.ts deleted file mode 100644 index 61e9b2be8b0ab..0000000000000 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/helpers.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { - Entity, - UserEntity, -} from '../../../../common/api/entity_analytics/entity_store/entities/common.gen'; - -export const isUserEntity = (record: Entity): record is UserEntity => - !!(record as UserEntity)?.user; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/helpers.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/helpers.tsx new file mode 100644 index 0000000000000..e339a63be7064 --- /dev/null +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/helpers.tsx @@ -0,0 +1,46 @@ +/* + * Copyright 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 { FormattedMessage } from '@kbn/i18n-react'; +import { + ASSET_CRITICALITY_INDEX_PATTERN, + RISK_SCORE_INDEX_PATTERN, +} from '../../../../common/constants'; +import type { + Entity, + UserEntity, +} from '../../../../common/api/entity_analytics/entity_store/entities/common.gen'; + +export const isUserEntity = (record: Entity): record is UserEntity => + !!(record as UserEntity)?.user; + +export const sourceFieldToText = (source: string) => { + if (source.match(`^${RISK_SCORE_INDEX_PATTERN}`)) { + return ( + + ); + } + + if (source.match(`^${ASSET_CRITICALITY_INDEX_PATTERN}`)) { + return ( + + ); + } + + return ( + + ); +}; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/hooks/use_entities_list_columns.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/hooks/use_entities_list_columns.tsx index 52439d10a0000..974a80454ee21 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/hooks/use_entities_list_columns.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/hooks/use_entities_list_columns.tsx @@ -17,9 +17,9 @@ import { RiskScoreLevel } from '../../severity/common'; import { getEmptyTagValue } from '../../../../common/components/empty_value'; import type { Columns } from '../../../../explore/components/paginated_table'; import type { Entity } from '../../../../../common/api/entity_analytics/entity_store/entities/common.gen'; -import type { CriticalityLevels } from '../../../../../common/constants'; +import { type CriticalityLevels } from '../../../../../common/constants'; import { ENTITIES_LIST_TABLE_ID } from '../constants'; -import { isUserEntity } from '../helpers'; +import { isUserEntity, sourceFieldToText } from '../helpers'; import { CRITICALITY_LEVEL_TITLE } from '../../asset_criticality/translations'; export type EntitiesListColumns = [ @@ -79,7 +79,7 @@ export const useEntitiesListColumns = (): EntitiesListColumns => { width: '5%', }, { - field: 'entity.displayName.keyword', + field: 'entity.name.text', name: ( { truncateText: { lines: 2 }, render: (source: string | undefined) => { if (source != null) { - return {source}; + return sourceFieldToText(source); } return getEmptyTagValue(); diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/hooks/use_entities_list_filters.test.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/hooks/use_entities_list_filters.test.ts index de5f706d4524c..cdf0583374538 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/hooks/use_entities_list_filters.test.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/hooks/use_entities_list_filters.test.ts @@ -11,7 +11,7 @@ import { useGlobalFilterQuery } from '../../../../common/hooks/use_global_filter import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import { CriticalityLevels } from '../../../../../common/constants'; import { RiskSeverity } from '../../../../../common/search_strategy'; -import { EntitySource } from '../components/entity_source_filter'; +import { EntitySourceTag } from '../types'; jest.mock('../../../../common/hooks/use_global_filter_query'); @@ -52,7 +52,6 @@ describe('useEntitiesListFilters', () => { { term: { 'host.risk.calculated_level': RiskSeverity.High } }, { term: { 'user.risk.calculated_level': RiskSeverity.High } }, ], - minimum_should_match: 1, }, }, ]; @@ -72,7 +71,6 @@ describe('useEntitiesListFilters', () => { const expectedFilters: QueryDslQueryContainer[] = [ { bool: { - minimum_should_match: 1, should: [ { term: { @@ -97,13 +95,48 @@ describe('useEntitiesListFilters', () => { useEntitiesListFilters({ selectedSeverities: [], selectedCriticalities: [], - selectedSources: [EntitySource.CSV_UPLOAD, EntitySource.EVENTS], + selectedSources: [EntitySourceTag.criticality, EntitySourceTag.risk], }) ); const expectedFilters: QueryDslQueryContainer[] = [ - { term: { 'entity.source': EntitySource.CSV_UPLOAD } }, - { term: { 'entity.source': EntitySource.EVENTS } }, + { + bool: { + should: [ + { wildcard: { 'entity.source': '.asset-criticality.asset-criticality-*' } }, + { wildcard: { 'entity.source': 'risk-score.risk-score-*' } }, + ], + }, + }, + ]; + + expect(result.current).toEqual(expectedFilters); + }); + + it('should return source events filters when events is selected', () => { + const { result } = renderHook(() => + useEntitiesListFilters({ + selectedSeverities: [], + selectedCriticalities: [], + selectedSources: [EntitySourceTag.events], + }) + ); + + const expectedFilters: QueryDslQueryContainer[] = [ + { + bool: { + should: [ + { + bool: { + must_not: [ + { wildcard: { 'entity.source': '.asset-criticality.asset-criticality-*' } }, + { wildcard: { 'entity.source': 'risk-score.risk-score-*' } }, + ], + }, + }, + ], + }, + }, ]; expect(result.current).toEqual(expectedFilters); @@ -132,7 +165,7 @@ describe('useEntitiesListFilters', () => { useEntitiesListFilters({ selectedSeverities: [RiskSeverity.Low], selectedCriticalities: [CriticalityLevels.HIGH_IMPACT], - selectedSources: [EntitySource.CSV_UPLOAD], + selectedSources: [EntitySourceTag.risk], }) ); @@ -143,16 +176,18 @@ describe('useEntitiesListFilters', () => { { term: { 'host.risk.calculated_level': RiskSeverity.Low } }, { term: { 'user.risk.calculated_level': RiskSeverity.Low } }, ], - minimum_should_match: 1, }, }, { bool: { should: [{ term: { 'asset.criticality': CriticalityLevels.HIGH_IMPACT } }], - minimum_should_match: 1, }, }, - { term: { 'entity.source': EntitySource.CSV_UPLOAD } }, + { + bool: { + should: [{ wildcard: { 'entity.source': 'risk-score.risk-score-*' } }], + }, + }, globalQuery, ]; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/hooks/use_entities_list_filters.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/hooks/use_entities_list_filters.ts index 634f3f61c1590..ba720025f4a59 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/hooks/use_entities_list_filters.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/hooks/use_entities_list_filters.ts @@ -7,15 +7,19 @@ import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import { useMemo } from 'react'; -import type { CriticalityLevels } from '../../../../../common/constants'; +import { + ASSET_CRITICALITY_INDEX_PATTERN, + RISK_SCORE_INDEX_PATTERN, + type CriticalityLevels, +} from '../../../../../common/constants'; import type { RiskSeverity } from '../../../../../common/search_strategy'; import { useGlobalFilterQuery } from '../../../../common/hooks/use_global_filter_query'; -import type { EntitySource } from '../components/entity_source_filter'; +import { EntitySourceTag } from '../types'; interface UseEntitiesListFiltersParams { selectedSeverities: RiskSeverity[]; selectedCriticalities: CriticalityLevels[]; - selectedSources: EntitySource[]; + selectedSources: EntitySourceTag[]; } export const useEntitiesListFilters = ({ @@ -35,17 +39,20 @@ export const useEntitiesListFilters = ({ 'asset.criticality': value, }, })), - minimum_should_match: 1, }, }, ] : []; - const sourceFilter: QueryDslQueryContainer[] = selectedSources.map((value) => ({ - term: { - 'entity.source': value, - }, - })); + const sourceFilter: QueryDslQueryContainer[] = selectedSources.length + ? [ + { + bool: { + should: selectedSources.map((tag) => getSourceTagFilterQuery(tag)), + }, + }, + ] + : []; const severityFilter: QueryDslQueryContainer[] = selectedSeverities.length ? [ @@ -63,7 +70,6 @@ export const useEntitiesListFilters = ({ }, }, ]), - minimum_should_match: 1, }, }, ] @@ -80,3 +86,37 @@ export const useEntitiesListFilters = ({ return filterList; }, [globalQuery, selectedCriticalities, selectedSeverities, selectedSources]); }; + +const getSourceTagFilterQuery = (tag: EntitySourceTag): QueryDslQueryContainer => { + if (tag === EntitySourceTag.risk) { + return { + wildcard: { + 'entity.source': RISK_SCORE_INDEX_PATTERN, + }, + }; + } + if (tag === EntitySourceTag.criticality) { + return { + wildcard: { + 'entity.source': ASSET_CRITICALITY_INDEX_PATTERN, + }, + }; + } + + return { + bool: { + must_not: [ + { + wildcard: { + 'entity.source': ASSET_CRITICALITY_INDEX_PATTERN, + }, + }, + { + wildcard: { + 'entity.source': RISK_SCORE_INDEX_PATTERN, + }, + }, + ], + }, + }; +}; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/types.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/types.ts new file mode 100644 index 0000000000000..0adabf36eb436 --- /dev/null +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/types.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 enum EntitySourceTag { + 'risk' = 'Risk', + 'criticality' = 'Asset Criticality', + 'events' = 'Events', +} diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/attach_to_active_timeline.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/attach_to_active_timeline.test.tsx index 610354f8af822..51f5641af0f9c 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/attach_to_active_timeline.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/attach_to_active_timeline.test.tsx @@ -50,13 +50,13 @@ describe('AttachToActiveTimeline', () => { expect(getByTestId(SAVE_TIMELINE_BUTTON_TEST_ID)).toBeInTheDocument(); expect(getByTestId(SAVE_TIMELINE_BUTTON_TEST_ID)).toHaveStyle('background-color: #FEC514'); - expect(getByTestId(SAVE_TIMELINE_BUTTON_TEST_ID)).toHaveTextContent('Save timeline'); + expect(getByTestId(SAVE_TIMELINE_BUTTON_TEST_ID)).toHaveTextContent('Save current Timeline'); expect(queryByTestId(ATTACH_TO_TIMELINE_CHECKBOX_TEST_ID)).not.toBeInTheDocument(); expect(getByTestId(ATTACH_TO_TIMELINE_CALLOUT_TEST_ID)).toBeInTheDocument(); expect(getByTestId(ATTACH_TO_TIMELINE_CALLOUT_TEST_ID)).toHaveClass('euiCallOut--warning'); - expect(getByText('Attach to timeline')).toBeInTheDocument(); + expect(getByText('Attach to current Timeline')).toBeInTheDocument(); expect( - getByText('Before attaching a note to the timeline, you need to save the timeline first.') + getByText('You must save the current Timeline before attaching notes to it.') ).toBeInTheDocument(); }); @@ -76,7 +76,7 @@ describe('AttachToActiveTimeline', () => { }, }); - const { getByTestId, getByText, queryByTestId } = render( + const { getByTestId, getByText, getAllByText, queryByTestId } = render( { expect(getByTestId(ATTACH_TO_TIMELINE_CHECKBOX_TEST_ID)).toBeInTheDocument(); expect(getByTestId(ATTACH_TO_TIMELINE_CALLOUT_TEST_ID)).toBeInTheDocument(); expect(getByTestId(ATTACH_TO_TIMELINE_CALLOUT_TEST_ID)).toHaveClass('euiCallOut--primary'); - expect(getByText('Attach to timeline')).toBeInTheDocument(); - expect( - getByText('You can associate the newly created note to the active timeline.') - ).toBeInTheDocument(); + expect(getAllByText('Attach to current Timeline')).toHaveLength(2); + expect(getByText('Also attach this note to the current Timeline.')).toBeInTheDocument(); }); it('should call the callback when user click on the checkbox', () => { diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/attach_to_active_timeline.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/attach_to_active_timeline.tsx index 77b3404561275..c8cd360947881 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/attach_to_active_timeline.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/attach_to_active_timeline.tsx @@ -26,31 +26,31 @@ const timelineCheckBoxId = 'xpack.securitySolution.flyout.notes.attachToTimeline export const ATTACH_TO_TIMELINE_CALLOUT_TITLE = i18n.translate( 'xpack.securitySolution.flyout.left.notes.attachToTimeline.calloutTitle', { - defaultMessage: 'Attach to timeline', + defaultMessage: 'Attach to current Timeline', } ); export const SAVED_TIMELINE_CALLOUT_CONTENT = i18n.translate( 'xpack.securitySolution.flyout.left.notes.attachToTimeline.calloutContent', { - defaultMessage: 'You can associate the newly created note to the active timeline.', + defaultMessage: 'Also attach this note to the current Timeline.', } ); export const UNSAVED_TIMELINE_CALLOUT_CONTENT = i18n.translate( 'xpack.securitySolution.flyout.left.notes.attachToTimeline.calloutContent', { - defaultMessage: 'Before attaching a note to the timeline, you need to save the timeline first.', + defaultMessage: 'You must save the current Timeline before attaching notes to it.', } ); export const ATTACH_TO_TIMELINE_CHECKBOX = i18n.translate( 'xpack.securitySolution.flyout.left.notes.attachToTimeline.checkboxLabel', { - defaultMessage: 'Attach to active timeline', + defaultMessage: 'Attach to current Timeline', } ); export const SAVE_TIMELINE_BUTTON = i18n.translate( - 'xpack.securitySolution.flyout.left.notes.savedTimelineButtonLabel', + 'xpack.securitySolution.flyout.left.notes.attachToTimeline.savedTimelineButtonLabel', { - defaultMessage: 'Save timeline', + defaultMessage: 'Save current Timeline', } ); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/notes_details.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/notes_details.tsx index f97ca576d2385..8877e1759c475 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/notes_details.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/notes_details.tsx @@ -33,6 +33,8 @@ import { import { useDocumentDetailsContext } from '../../shared/context'; import { useUserPrivileges } from '../../../../common/components/user_privileges'; import { useWhichFlyout } from '../../shared/hooks/use_which_flyout'; +import { BasicAlertDataContext } from './investigation_guide_view'; +import { useInvestigationGuide } from '../../shared/hooks/use_investigation_guide'; export const FETCH_NOTES_ERROR = i18n.translate( 'xpack.securitySolution.flyout.left.notes.fetchNotesErrorLabel', @@ -42,7 +44,7 @@ export const FETCH_NOTES_ERROR = i18n.translate( ); export const NO_NOTES = (isAlert: boolean) => i18n.translate('xpack.securitySolution.flyout.left.notes.noNotesLabel', { - defaultMessage: 'No notes have been created for this {value}', + defaultMessage: 'No notes have been created for this {value}.', values: { value: isAlert ? 'alert' : 'event' }, }); @@ -55,6 +57,10 @@ export const NotesDetails = memo(() => { const dispatch = useDispatch(); const { eventId, dataFormattedForFieldBrowser } = useDocumentDetailsContext(); const { kibanaSecuritySolutionsPrivileges } = useUserPrivileges(); + const { basicAlertData: basicData } = useInvestigationGuide({ + dataFormattedForFieldBrowser, + }); + const canCreateNotes = kibanaSecuritySolutionsPrivileges.crud; // will drive the value we send to the AddNote component @@ -130,7 +136,7 @@ export const NotesDetails = memo(() => { ); return ( - <> + {fetchStatus === ReqStatus.Loading && ( )} @@ -156,7 +162,7 @@ export const NotesDetails = memo(() => { )} - + ); }); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_users_filter.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_users_filter.test.tsx index afcd051af1bd8..535c0114426dd 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_users_filter.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_users_filter.test.tsx @@ -27,7 +27,7 @@ describe('Users filter', () => { const filterPrefix = 'users-filter'; let onChangeUsersFilter: jest.Mock; - beforeEach(async () => { + beforeEach(() => { onChangeUsersFilter = jest.fn(); mockedContext = createAppRootMockRenderer(); ({ history } = mockedContext); @@ -56,8 +56,8 @@ describe('Users filter', () => { render(); const searchInput = renderResult.getByTestId(`${testPrefix}-${filterPrefix}-search`); - await userEvent.type(searchInput, 'usernameX'); - await userEvent.type(searchInput, '{enter}'); + await userEvent.type(searchInput, 'usernameX', { delay: 10 }); + await userEvent.keyboard('{enter}'); expect(onChangeUsersFilter).toHaveBeenCalledWith(['usernameX']); }); @@ -65,8 +65,8 @@ describe('Users filter', () => { render(); const searchInput = renderResult.getByTestId(`${testPrefix}-${filterPrefix}-search`); - await userEvent.type(searchInput, 'usernameX,usernameY,usernameZ'); - await userEvent.type(searchInput, '{enter}'); + await userEvent.type(searchInput, 'usernameX,usernameY,usernameZ', { delay: 10 }); + await userEvent.keyboard('{enter}'); expect(onChangeUsersFilter).toHaveBeenCalledWith(['usernameX', 'usernameY', 'usernameZ']); }); @@ -74,8 +74,8 @@ describe('Users filter', () => { render(); const searchInput = renderResult.getByTestId(`${testPrefix}-${filterPrefix}-search`); - await userEvent.type(searchInput, ' usernameX '); - await userEvent.type(searchInput, '{enter}'); + await userEvent.type(searchInput, ' usernameX ', { delay: 10 }); + await userEvent.keyboard('{enter}'); expect(onChangeUsersFilter).toHaveBeenCalledWith(['usernameX']); }); @@ -83,8 +83,8 @@ describe('Users filter', () => { render(); const searchInput = renderResult.getByTestId(`${testPrefix}-${filterPrefix}-search`); - await userEvent.type(searchInput, ' , usernameX ,usernameY , '); - await userEvent.type(searchInput, '{enter}'); + await userEvent.type(searchInput, ' , usernameX ,usernameY , ', { delay: 10 }); + await userEvent.keyboard('{enter}'); expect(onChangeUsersFilter).toHaveBeenCalledWith(['usernameX', 'usernameY']); }); }); diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/endpoint_list/endpoints.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/endpoint_list/endpoints.cy.ts index 03b9797abb56e..12cdfcfa6e09c 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/endpoint_list/endpoints.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/endpoint_list/endpoints.cy.ts @@ -28,8 +28,7 @@ import { createEndpointHost } from '../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../tasks/delete_all_endpoint_data'; import { enableAllPolicyProtections } from '../../tasks/endpoint_policy'; -// Failing: See https://github.com/elastic/kibana/issues/195476 -describe.skip('Endpoints page', { tags: ['@ess', '@serverless'] }, () => { +describe('Endpoints page', { tags: ['@ess', '@serverless'] }, () => { let indexedPolicy: IndexedFleetEndpointPolicyResponse; let policy: PolicyData; let createdHost: CreateAndEnrollEndpointHostResponse; diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/rbac/endpoint_role_rbac_with_space_awareness.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/rbac/endpoint_role_rbac_with_space_awareness.cy.ts index 2a3d2876ea488..424b3fc954c57 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/rbac/endpoint_role_rbac_with_space_awareness.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/rbac/endpoint_role_rbac_with_space_awareness.cy.ts @@ -23,8 +23,7 @@ import { setSecuritySolutionEndpointGroupPrivilege, } from '../../screens/stack_management/role_page'; -// FLAKY: https://github.com/elastic/kibana/issues/195477 -describe.skip( +describe( 'When defining a kibana role for Endpoint security access with space awareness enabled', { // TODO:PR Remove `'@skipInServerlessMKI` once PR merges to `main` diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_actions_history.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_actions_history.cy.ts index 93c6f0698a81a..dfd67d6854b63 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_actions_history.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_actions_history.cy.ts @@ -10,8 +10,7 @@ import { indexEndpointHosts } from '../../tasks/index_endpoint_hosts'; import { login } from '../../tasks/login'; import { loadPage } from '../../tasks/common'; -// FLAKY: https://github.com/elastic/kibana/issues/172549 -describe.skip( +describe( 'Response actions history page', { tags: ['@ess', '@serverless', '@skipInServerlessMKI'] }, () => { diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/release.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/release.cy.ts index 96702227b70b9..d11b7210713a8 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/release.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/release.cy.ts @@ -27,8 +27,7 @@ import { enableAllPolicyProtections } from '../../../tasks/endpoint_policy'; import { createEndpointHost } from '../../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../../tasks/delete_all_endpoint_data'; -// Failing: See https://github.com/elastic/kibana/issues/172418 -describe.skip('Response console', { tags: ['@ess', '@serverless'] }, () => { +describe('Response console', { tags: ['@ess', '@serverless'] }, () => { let indexedPolicy: IndexedFleetEndpointPolicyResponse; let policy: PolicyData; let createdHost: CreateAndEnrollEndpointHostResponse; diff --git a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_list_artifact.test.tsx b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_list_artifact.test.tsx index 8f6e5c4353206..52120153dd079 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_list_artifact.test.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_list_artifact.test.tsx @@ -16,7 +16,8 @@ import { renderQuery, } from '../test_utils'; -describe('List artifact hook', () => { +// FLAKY: https://github.com/elastic/kibana/issues/196724 +describe.skip('List artifact hook', () => { let result: ReturnType; let searchableFields: string[]; let options: diff --git a/x-pack/plugins/security_solution/public/management/links.ts b/x-pack/plugins/security_solution/public/management/links.ts index c83a7360910fa..27b5b62eac6f8 100644 --- a/x-pack/plugins/security_solution/public/management/links.ts +++ b/x-pack/plugins/security_solution/public/management/links.ts @@ -223,7 +223,7 @@ export const links: LinkItem = { title: NOTES, description: i18n.translate('xpack.securitySolution.appLinks.notesDescription', { defaultMessage: - 'Oversee, revise and revisit the annotations within each document and timeline.', + 'Oversee, revise, and revisit the notes attached to alerts, events and Timelines.', }), landingIcon: 'filebeatApp', path: NOTES_PATH, diff --git a/x-pack/plugins/security_solution/public/notes/components/open_flyout_button.tsx b/x-pack/plugins/security_solution/public/notes/components/open_flyout_button.tsx index 34ae9405fdf86..85e9e24c6f26e 100644 --- a/x-pack/plugins/security_solution/public/notes/components/open_flyout_button.tsx +++ b/x-pack/plugins/security_solution/public/notes/components/open_flyout_button.tsx @@ -20,7 +20,7 @@ import { DocumentDetailsRightPanelKey } from '../../flyout/document_details/shar export const OPEN_FLYOUT_BUTTON = i18n.translate( 'xpack.securitySolution.notes.openFlyoutButtonLabel', { - defaultMessage: 'Expand event details', + defaultMessage: 'Expand alert/event details', } ); diff --git a/x-pack/plugins/security_solution/public/notes/components/search_row.test.tsx b/x-pack/plugins/security_solution/public/notes/components/search_row.test.tsx index be9546c77525b..447ade158306b 100644 --- a/x-pack/plugins/security_solution/public/notes/components/search_row.test.tsx +++ b/x-pack/plugins/security_solution/public/notes/components/search_row.test.tsx @@ -5,13 +5,14 @@ * 2.0. */ -import { fireEvent, render, screen } from '@testing-library/react'; +import { render } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { SearchRow } from './search_row'; import { ASSOCIATED_NOT_SELECT_TEST_ID, SEARCH_BAR_TEST_ID, USER_SELECT_TEST_ID } from './test_ids'; import { AssociatedFilter } from '../../../common/notes/constants'; import { useSuggestUsers } from '../../common/components/user_profiles/use_suggest_users'; +import { TestProviders } from '../../common/mock'; jest.mock('../../common/components/user_profiles/use_suggest_users'); @@ -35,7 +36,11 @@ describe('SearchRow', () => { }); it('should render the component', () => { - const { getByTestId } = render(); + const { getByTestId } = render( + + + + ); expect(getByTestId(SEARCH_BAR_TEST_ID)).toBeInTheDocument(); expect(getByTestId(USER_SELECT_TEST_ID)).toBeInTheDocument(); @@ -43,7 +48,11 @@ describe('SearchRow', () => { }); it('should call the correct action when entering a value in the search bar', async () => { - const { getByTestId } = render(); + const { getByTestId } = render( + + + + ); const searchBox = getByTestId(SEARCH_BAR_TEST_ID); @@ -53,20 +62,12 @@ describe('SearchRow', () => { expect(mockDispatch).toHaveBeenCalled(); }); - it('should call the correct action when select a user', async () => { - const { getByTestId } = render(); - - const userSelect = getByTestId('comboBoxSearchInput'); - userSelect.focus(); - - const option = await screen.findByText('test'); - fireEvent.click(option); - - expect(mockDispatch).toHaveBeenCalled(); - }); - it('should call the correct action when select a value in the associated note dropdown', async () => { - const { getByTestId } = render(); + const { getByTestId } = render( + + + + ); const associatedNoteSelect = getByTestId(ASSOCIATED_NOT_SELECT_TEST_ID); await userEvent.selectOptions(associatedNoteSelect, [AssociatedFilter.documentOnly]); diff --git a/x-pack/plugins/security_solution/public/notes/components/search_row.tsx b/x-pack/plugins/security_solution/public/notes/components/search_row.tsx index d540a586814d8..3c4093f913acf 100644 --- a/x-pack/plugins/security_solution/public/notes/components/search_row.tsx +++ b/x-pack/plugins/security_solution/public/notes/components/search_row.tsx @@ -5,10 +5,9 @@ * 2.0. */ -import React, { useMemo, useCallback, useState } from 'react'; +import React, { useCallback } from 'react'; import type { EuiSelectOption } from '@elastic/eui'; import { - EuiComboBox, EuiFlexGroup, EuiFlexItem, EuiSearchBar, @@ -16,19 +15,14 @@ import { useGeneratedHtmlId, } from '@elastic/eui'; import { useDispatch } from 'react-redux'; -import type { UserProfileWithAvatar } from '@kbn/user-profile-components'; import { i18n } from '@kbn/i18n'; -import type { EuiComboBoxOptionOption } from '@elastic/eui/src/components/combo_box/types'; -import { ASSOCIATED_NOT_SELECT_TEST_ID, SEARCH_BAR_TEST_ID, USER_SELECT_TEST_ID } from './test_ids'; -import { useSuggestUsers } from '../../common/components/user_profiles/use_suggest_users'; -import { userFilterAssociatedNotes, userFilterUsers, userSearchedNotes } from '..'; +import { UserFilterDropdown } from './user_filter_dropdown'; +import { ASSOCIATED_NOT_SELECT_TEST_ID, SEARCH_BAR_TEST_ID } from './test_ids'; +import { userFilterAssociatedNotes, userSearchedNotes } from '..'; import { AssociatedFilter } from '../../../common/notes/constants'; -export const USERS_DROPDOWN = i18n.translate('xpack.securitySolution.notes.usersDropdownLabel', { - defaultMessage: 'Users', -}); -const FILTER_SELECT = i18n.translate('xpack.securitySolution.notes.management.filterSelect', { - defaultMessage: 'Select filter', +const ATTACH_FILTER = i18n.translate('xpack.securitySolution.notes.management.attachFilter', { + defaultMessage: 'Attached to', }); const searchBox = { @@ -37,11 +31,14 @@ const searchBox = { 'data-test-subj': SEARCH_BAR_TEST_ID, }; const associatedNoteSelectOptions: EuiSelectOption[] = [ - { value: AssociatedFilter.all, text: 'All' }, - { value: AssociatedFilter.documentOnly, text: 'Attached to document only' }, - { value: AssociatedFilter.savedObjectOnly, text: 'Attached to timeline only' }, - { value: AssociatedFilter.documentAndSavedObject, text: 'Attached to document and timeline' }, - { value: AssociatedFilter.orphan, text: 'Orphan' }, + { value: AssociatedFilter.all, text: 'Anything or nothing' }, + { value: AssociatedFilter.documentOnly, text: 'Alerts or events only' }, + { value: AssociatedFilter.savedObjectOnly, text: 'Timelines only' }, + { + value: AssociatedFilter.documentAndSavedObject, + text: 'Alerts or events and Timelines only', + }, + { value: AssociatedFilter.orphan, text: 'Nothing' }, ]; export const SearchRow = React.memo(() => { @@ -55,26 +52,6 @@ export const SearchRow = React.memo(() => { [dispatch] ); - const { isLoading: isLoadingSuggestedUsers, data: userProfiles } = useSuggestUsers({ - searchTerm: '', - }); - const users = useMemo( - () => - (userProfiles || []).map((userProfile: UserProfileWithAvatar) => ({ - label: userProfile.user.full_name || userProfile.user.username, - })), - [userProfiles] - ); - - const [selectedUser, setSelectedUser] = useState>>(); - const onChange = useCallback( - (user: Array>) => { - setSelectedUser(user); - dispatch(userFilterUsers(user.length > 0 ? user[0].label : '')); - }, - [dispatch] - ); - const onAssociatedNoteSelectChange = useCallback( (e: React.ChangeEvent) => { dispatch(userFilterAssociatedNotes(e.target.value as AssociatedFilter)); @@ -88,23 +65,15 @@ export const SearchRow = React.memo(() => { - + diff --git a/x-pack/plugins/security_solution/public/notes/components/user_filter_dropdown.test.tsx b/x-pack/plugins/security_solution/public/notes/components/user_filter_dropdown.test.tsx new file mode 100644 index 0000000000000..b095036e58632 --- /dev/null +++ b/x-pack/plugins/security_solution/public/notes/components/user_filter_dropdown.test.tsx @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { fireEvent, render, screen } from '@testing-library/react'; +import React from 'react'; +import { UserFilterDropdown } from './user_filter_dropdown'; +import { USER_SELECT_TEST_ID } from './test_ids'; +import { useSuggestUsers } from '../../common/components/user_profiles/use_suggest_users'; +import { useLicense } from '../../common/hooks/use_license'; +import { useUpsellingMessage } from '../../common/hooks/use_upselling'; + +jest.mock('../../common/components/user_profiles/use_suggest_users'); +jest.mock('../../common/hooks/use_license'); +jest.mock('../../common/hooks/use_upselling'); + +const mockDispatch = jest.fn(); +jest.mock('react-redux', () => { + const original = jest.requireActual('react-redux'); + + return { + ...original, + useDispatch: () => mockDispatch, + }; +}); + +describe('UserFilterDropdown', () => { + beforeEach(() => { + jest.clearAllMocks(); + (useSuggestUsers as jest.Mock).mockReturnValue({ + isLoading: false, + data: [{ user: { username: 'test' } }, { user: { username: 'elastic' } }], + }); + (useLicense as jest.Mock).mockReturnValue({ isPlatinumPlus: () => true }); + (useUpsellingMessage as jest.Mock).mockReturnValue('upsellingMessage'); + }); + + it('should render the component enabled', () => { + const { getByTestId } = render(); + + const dropdown = getByTestId(USER_SELECT_TEST_ID); + + expect(dropdown).toBeInTheDocument(); + expect(dropdown).not.toHaveClass('euiComboBox-isDisabled'); + }); + + it('should render the dropdown disabled', async () => { + (useLicense as jest.Mock).mockReturnValue({ isPlatinumPlus: () => false }); + + const { getByTestId } = render(); + + expect(getByTestId(USER_SELECT_TEST_ID)).toHaveClass('euiComboBox-isDisabled'); + }); + + it('should call the correct action when select a user', async () => { + const { getByTestId } = render(); + + const userSelect = getByTestId('comboBoxSearchInput'); + userSelect.focus(); + + const option = await screen.findByText('test'); + fireEvent.click(option); + + expect(mockDispatch).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/notes/components/user_filter_dropdown.tsx b/x-pack/plugins/security_solution/public/notes/components/user_filter_dropdown.tsx new file mode 100644 index 0000000000000..78f4ef6dd2ac8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/notes/components/user_filter_dropdown.tsx @@ -0,0 +1,79 @@ +/* + * Copyright 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, { useMemo, useCallback, useState } from 'react'; +import { EuiComboBox, EuiToolTip } from '@elastic/eui'; +import { useDispatch } from 'react-redux'; +import type { UserProfileWithAvatar } from '@kbn/user-profile-components'; +import { i18n } from '@kbn/i18n'; +import type { EuiComboBoxOptionOption } from '@elastic/eui/src/components/combo_box/types'; +import { useLicense } from '../../common/hooks/use_license'; +import { useUpsellingMessage } from '../../common/hooks/use_upselling'; +import { USER_SELECT_TEST_ID } from './test_ids'; +import { useSuggestUsers } from '../../common/components/user_profiles/use_suggest_users'; +import { userFilterUsers } from '..'; + +export const USERS_DROPDOWN = i18n.translate('xpack.securitySolution.notes.usersDropdownLabel', { + defaultMessage: 'Users', +}); + +export const UserFilterDropdown = React.memo(() => { + const dispatch = useDispatch(); + const isPlatinumPlus = useLicense().isPlatinumPlus(); + const upsellingMessage = useUpsellingMessage('note_management_user_filter'); + + const { isLoading, data } = useSuggestUsers({ + searchTerm: '', + enabled: isPlatinumPlus, + }); + const users = useMemo( + () => + (data || []).map((userProfile: UserProfileWithAvatar) => ({ + label: userProfile.user.full_name || userProfile.user.username, + })), + [data] + ); + + const [selectedUser, setSelectedUser] = useState>>(); + const onChange = useCallback( + (user: Array>) => { + setSelectedUser(user); + dispatch(userFilterUsers(user.length > 0 ? user[0].label : '')); + }, + [dispatch] + ); + + const dropdown = useMemo( + () => ( + + ), + [isLoading, isPlatinumPlus, onChange, selectedUser, users] + ); + + return ( + <> + {isPlatinumPlus ? ( + <>{dropdown} + ) : ( + + {dropdown} + + )} + + ); +}); + +UserFilterDropdown.displayName = 'UserFilterDropdown'; diff --git a/x-pack/plugins/observability_solution/inventory/.storybook/main.js b/x-pack/plugins/security_solution/public/onboarding/common/lib/__mocks__/telemetry.ts similarity index 81% rename from x-pack/plugins/observability_solution/inventory/.storybook/main.js rename to x-pack/plugins/security_solution/public/onboarding/common/lib/__mocks__/telemetry.ts index 86b48c32f103e..5d1c3feb56ed9 100644 --- a/x-pack/plugins/observability_solution/inventory/.storybook/main.js +++ b/x-pack/plugins/security_solution/public/onboarding/common/lib/__mocks__/telemetry.ts @@ -5,4 +5,4 @@ * 2.0. */ -module.exports = require('@kbn/storybook').defaultConfig; +export const trackOnboardingLinkClick = jest.fn(); diff --git a/x-pack/plugins/security_solution/public/onboarding/common/lib/telemetry.ts b/x-pack/plugins/security_solution/public/onboarding/common/lib/telemetry.ts new file mode 100644 index 0000000000000..a88ae651ae600 --- /dev/null +++ b/x-pack/plugins/security_solution/public/onboarding/common/lib/telemetry.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. + */ + +import { METRIC_TYPE, TELEMETRY_EVENT, track } from '../../../common/lib/telemetry'; + +export const trackOnboardingLinkClick = (linkId: string) => { + track(METRIC_TYPE.CLICK, `${TELEMETRY_EVENT.ONBOARDING}_${linkId}`); +}; diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/index.ts b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/index.ts index bf4195b814590..27deda4190f2e 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/index.ts +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/index.ts @@ -25,6 +25,8 @@ export const assistantCardConfig: OnboardingCardConfig = ) ), checkComplete: checkAssistantCardComplete, - capabilities: 'securitySolutionAssistant.ai-assistant', + // Both capabilities are needed for this card, so we should use a double array to create an AND conditional + // (a single array would create an OR conditional between them) + capabilities: [['securitySolutionAssistant.ai-assistant', 'actions.show']], licenseType: 'enterprise', }; diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/callouts/agent_required_callout.test.tsx b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/callouts/agent_required_callout.test.tsx index dbd0c105d27a1..53e8b6c34e8f2 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/callouts/agent_required_callout.test.tsx +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/callouts/agent_required_callout.test.tsx @@ -14,8 +14,10 @@ import React from 'react'; import { render } from '@testing-library/react'; import { AgentRequiredCallout } from './agent_required_callout'; import { TestProviders } from '../../../../../../common/mock/test_providers'; +import { trackOnboardingLinkClick } from '../../../../../common/lib/telemetry'; jest.mock('../../../../../../common/lib/kibana'); +jest.mock('../../../../../common/lib/telemetry'); describe('AgentRequiredCallout', () => { beforeEach(() => { @@ -30,4 +32,12 @@ describe('AgentRequiredCallout', () => { ).toBeInTheDocument(); expect(getByTestId('agentLink')).toBeInTheDocument(); }); + + it('should track the agent link click', () => { + const { getByTestId } = render(, { wrapper: TestProviders }); + + getByTestId('agentLink').click(); + + expect(trackOnboardingLinkClick).toHaveBeenCalledWith('agent_required'); + }); }); diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/callouts/agent_required_callout.tsx b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/callouts/agent_required_callout.tsx index aad22c959bc65..b1d18b138487b 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/callouts/agent_required_callout.tsx +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/callouts/agent_required_callout.tsx @@ -11,16 +11,22 @@ import { EuiIcon } from '@elastic/eui'; import { LinkAnchor } from '../../../../../../common/components/links'; import { CardCallOut } from '../../common/card_callout'; import { useNavigation } from '../../../../../../common/lib/kibana'; -import { FLEET_APP_ID, ADD_AGENT_PATH } from '../constants'; +import { FLEET_APP_ID, ADD_AGENT_PATH, TELEMETRY_AGENT_REQUIRED } from '../constants'; +import { trackOnboardingLinkClick } from '../../../../../common/lib/telemetry'; const fleetAgentLinkProps = { appId: FLEET_APP_ID, path: ADD_AGENT_PATH }; export const AgentRequiredCallout = React.memo(() => { const { getAppUrl, navigateTo } = useNavigation(); const addAgentLink = getAppUrl(fleetAgentLinkProps); - const onAddAgentClick = useCallback(() => { - navigateTo(fleetAgentLinkProps); - }, [navigateTo]); + const onAddAgentClick = useCallback( + (e: React.MouseEvent) => { + e.preventDefault(); + trackOnboardingLinkClick(TELEMETRY_AGENT_REQUIRED); + navigateTo(fleetAgentLinkProps); + }, + [navigateTo] + ); return ( ({ - useKibana: jest.fn(), -})); +jest.mock('../../../../../../common/lib/kibana'); +jest.mock('../../../../../common/lib/telemetry'); describe('AgentlessAvailableCallout', () => { const mockUseKibana = useKibana as jest.Mock; @@ -62,4 +62,14 @@ describe('AgentlessAvailableCallout', () => { ).toBeInTheDocument(); expect(getByTestId('agentlessLearnMoreLink')).toBeInTheDocument(); }); + + it('should track the agentless learn more link click', () => { + const { getByTestId } = render(, { + wrapper: TestProviders, + }); + + getByTestId('agentlessLearnMoreLink').click(); + + expect(trackOnboardingLinkClick).toHaveBeenCalledWith('agentless_learn_more'); + }); }); diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/callouts/agentless_available_callout.tsx b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/callouts/agentless_available_callout.tsx index f802f83efb7e5..eaf8cbaa3b287 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/callouts/agentless_available_callout.tsx +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/callouts/agentless_available_callout.tsx @@ -5,19 +5,25 @@ * 2.0. */ -import React from 'react'; +import React, { useCallback } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiIcon, useEuiTheme } from '@elastic/eui'; import { css } from '@emotion/react'; import { useKibana } from '../../../../../../common/lib/kibana'; import { LinkAnchor } from '../../../../../../common/components/links'; +import { trackOnboardingLinkClick } from '../../../../../common/lib/telemetry'; import { CardCallOut } from '../../common/card_callout'; +import { TELEMETRY_AGENTLESS_LEARN_MORE } from '../constants'; export const AgentlessAvailableCallout = React.memo(() => { const { euiTheme } = useEuiTheme(); const { docLinks } = useKibana().services; + const onClick = useCallback(() => { + trackOnboardingLinkClick(TELEMETRY_AGENTLESS_LEARN_MORE); + }, []); + /* @ts-expect-error: add the blog link to `packages/kbn-doc-links/src/get_doc_links.ts` when it is ready and remove this exit condition*/ if (!docLinks.links.fleet.agentlessBlog) { return null; @@ -54,6 +60,7 @@ export const AgentlessAvailableCallout = React.memo(() => { { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders the callout', () => { + const { getByTestId, getByText } = render(, { wrapper: TestProviders }); + + expect( + getByText('Orchestrate response across endpoint vendors with bidirectional integrations') + ).toBeInTheDocument(); + expect(getByTestId('endpointLearnMoreLink')).toBeInTheDocument(); + }); + + it('should track the agent link click', () => { + const { getByTestId } = render(, { wrapper: TestProviders }); + + getByTestId('endpointLearnMoreLink').click(); + + expect(trackOnboardingLinkClick).toHaveBeenCalledWith('endpoint_learn_more'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/callouts/endpoint_callout.tsx b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/callouts/endpoint_callout.tsx index 2ff48a1992d1d..d5b0199c9f401 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/callouts/endpoint_callout.tsx +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/callouts/endpoint_callout.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React from 'react'; +import React, { useCallback } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiIcon, useEuiTheme } from '@elastic/eui'; import { css } from '@emotion/react'; @@ -13,10 +13,15 @@ import { css } from '@emotion/react'; import { useKibana } from '../../../../../../common/lib/kibana/kibana_react'; import { LinkAnchor } from '../../../../../../common/components/links'; import { CardCallOut } from '../../common/card_callout'; +import { trackOnboardingLinkClick } from '../../../../../common/lib/telemetry'; +import { TELEMETRY_ENDPOINT_LEARN_MORE } from '../constants'; export const EndpointCallout = React.memo(() => { const { euiTheme } = useEuiTheme(); const { docLinks } = useKibana().services; + const onClick = useCallback(() => { + trackOnboardingLinkClick(TELEMETRY_ENDPOINT_LEARN_MORE); + }, []); return ( { data-test-subj="endpointLearnMoreLink" external={true} target="_blank" + onClick={onClick} > { const { href: integrationUrl, onClick: onAddIntegrationClicked } = useAddIntegrationsUrl(); + const onClick = useCallback( + (e: React.MouseEvent) => { + e.preventDefault(); + trackOnboardingLinkClick(TELEMETRY_MANAGE_INTEGRATIONS); + onAddIntegrationClicked(e); + }, + [onAddIntegrationClicked] + ); + if (!installedIntegrationsCount) { return null; } @@ -41,7 +52,7 @@ export const ManageIntegrationsCallout = React.memo( ), link: ( diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/constants.ts b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/constants.ts index e245de6129478..c748f5205e7aa 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/constants.ts +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/constants.ts @@ -24,3 +24,9 @@ export const SCROLL_ELEMENT_ID = 'integrations-scroll-container'; export const SEARCH_FILTER_CATEGORIES: CategoryFacet[] = []; export const WITH_SEARCH_BOX_HEIGHT = '568px'; export const WITHOUT_SEARCH_BOX_HEIGHT = '513px'; +export const TELEMETRY_MANAGE_INTEGRATIONS = `manage_integrations`; +export const TELEMETRY_ENDPOINT_LEARN_MORE = `endpoint_learn_more`; +export const TELEMETRY_AGENTLESS_LEARN_MORE = `agentless_learn_more`; +export const TELEMETRY_AGENT_REQUIRED = `agent_required`; +export const TELEMETRY_INTEGRATION_CARD = `card`; +export const TELEMETRY_INTEGRATION_TAB = `tab`; diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/integration_card_grid_tabs.test.tsx b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/integration_card_grid_tabs.test.tsx index f55cc8cd50b2d..c88ffb6a598b7 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/integration_card_grid_tabs.test.tsx +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/integration_card_grid_tabs.test.tsx @@ -15,9 +15,11 @@ import { useStoredIntegrationTabId, } from '../../../../hooks/use_stored_state'; import { DEFAULT_TAB } from './constants'; +import { trackOnboardingLinkClick } from '../../../../common/lib/telemetry'; jest.mock('../../../onboarding_context'); jest.mock('../../../../hooks/use_stored_state'); +jest.mock('../../../../common/lib/telemetry'); jest.mock('../../../../../common/lib/kibana', () => ({ ...jest.requireActual('../../../../../common/lib/kibana'), @@ -118,6 +120,33 @@ describe('IntegrationsCardGridTabsComponent', () => { expect(mockSetTabId).toHaveBeenCalledWith('user'); }); + it('tracks the tab clicks', () => { + (useStoredIntegrationTabId as jest.Mock).mockReturnValue(['recommended', mockSetTabId]); + + mockUseAvailablePackages.mockReturnValue({ + isLoading: false, + filteredCards: [], + setCategory: mockSetCategory, + setSelectedSubCategory: mockSetSelectedSubCategory, + setSearchTerm: mockSetSearchTerm, + }); + + const { getByTestId } = render( + + ); + + const tabButton = getByTestId('user'); + + act(() => { + fireEvent.click(tabButton); + }); + + expect(trackOnboardingLinkClick).toHaveBeenCalledWith('tab_user'); + }); + it('renders no search tools when showSearchTools is false', async () => { mockUseAvailablePackages.mockReturnValue({ isLoading: false, diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/integration_card_grid_tabs.tsx b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/integration_card_grid_tabs.tsx index fc30fb0d6c617..e1ce7f5cdecf1 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/integration_card_grid_tabs.tsx +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/integration_card_grid_tabs.tsx @@ -21,6 +21,7 @@ import { LOADING_SKELETON_TEXT_LINES, SCROLL_ELEMENT_ID, SEARCH_FILTER_CATEGORIES, + TELEMETRY_INTEGRATION_TAB, WITHOUT_SEARCH_BOX_HEIGHT, WITH_SEARCH_BOX_HEIGHT, } from './constants'; @@ -28,6 +29,7 @@ import { INTEGRATION_TABS, INTEGRATION_TABS_BY_ID } from './integration_tabs_con import { useIntegrationCardList } from './use_integration_card_list'; import { IntegrationTabId } from './types'; import { IntegrationCardTopCallout } from './callouts/integration_card_top_callout'; +import { trackOnboardingLinkClick } from '../../../../common/lib/telemetry'; export interface IntegrationsCardGridTabsProps { installedIntegrationsCount: number; @@ -55,8 +57,10 @@ export const IntegrationsCardGridTabsComponent = React.memo { const id = stringId as IntegrationTabId; + const trackId = `${TELEMETRY_INTEGRATION_TAB}_${id}`; scrollElement.current?.scrollTo?.(0, 0); setSelectedTabIdToStorage(id); + trackOnboardingLinkClick(trackId); }, [setSelectedTabIdToStorage] ); diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/integrations_card.test.tsx b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/integrations_card.test.tsx index 3f79745182c5a..296d5391fd611 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/integrations_card.test.tsx +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/integrations_card.test.tsx @@ -28,4 +28,15 @@ describe('IntegrationsCard', () => { ); expect(getByTestId('loadingInstalledIntegrations')).toBeInTheDocument(); }); + + it('renders the content', () => { + const { queryByTestId } = render( + + ); + expect(queryByTestId('loadingInstalledIntegrations')).not.toBeInTheDocument(); + expect(queryByTestId('integrationsCardGridTabs')).toBeInTheDocument(); + }); }); diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/integrations_check_complete.test.ts b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/integrations_check_complete.test.ts index 3dd19d8868390..961f1981291b8 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/integrations_check_complete.test.ts +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/integrations_check_complete.test.ts @@ -16,6 +16,7 @@ jest.mock('rxjs', () => ({ })); describe('checkIntegrationsCardComplete', () => { + const mockLastValueFrom = lastValueFrom as jest.Mock; const mockHttpGet: jest.Mock = jest.fn(); const mockSearch: jest.Mock = jest.fn(); const mockService = { @@ -27,6 +28,11 @@ describe('checkIntegrationsCardComplete', () => { search: mockSearch, }, }, + notifications: { + toasts: { + addError: jest.fn(), + }, + }, } as unknown as StartServices; beforeEach(() => { @@ -38,7 +44,7 @@ describe('checkIntegrationsCardComplete', () => { items: [], }); - (lastValueFrom as jest.Mock).mockResolvedValue({ + mockLastValueFrom.mockResolvedValue({ rawResponse: { hits: { total: 0 }, }, @@ -60,7 +66,7 @@ describe('checkIntegrationsCardComplete', () => { items: [{ status: installationStatuses.Installed }], }); - (lastValueFrom as jest.Mock).mockResolvedValue({ + mockLastValueFrom.mockResolvedValue({ rawResponse: { hits: { total: 0 }, }, @@ -86,7 +92,7 @@ describe('checkIntegrationsCardComplete', () => { ], }); - (lastValueFrom as jest.Mock).mockResolvedValue({ + mockLastValueFrom.mockResolvedValue({ rawResponse: { hits: { total: 1 }, }, @@ -103,4 +109,43 @@ describe('checkIntegrationsCardComplete', () => { }, }); }); + + it('renders an error toast when fetching integrations data fails', async () => { + const err = new Error('Failed to fetch integrations data'); + mockHttpGet.mockRejectedValue(err); + + const res = await checkIntegrationsCardComplete(mockService); + + expect(mockService.notifications.toasts.addError).toHaveBeenCalledWith(err, { + title: 'Error fetching integrations data', + }); + expect(res).toEqual({ + isComplete: false, + metadata: { + installedIntegrationsCount: 0, + isAgentRequired: false, + }, + }); + }); + + it('renders an error toast when fetching agents data fails', async () => { + const err = new Error('Failed to fetch agents data'); + mockLastValueFrom.mockRejectedValue(err); + + const res = await checkIntegrationsCardComplete(mockService); + + expect(mockService.notifications.toasts.addError).toHaveBeenCalledWith( + new Error('Failed to fetch agents data'), + { + title: 'Error fetching agents data', + } + ); + expect(res).toEqual({ + isComplete: false, + metadata: { + installedIntegrationsCount: 0, + isAgentRequired: false, + }, + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/integrations_check_complete.ts b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/integrations_check_complete.ts index 912b81bddf3fb..d4193dd8b9ded 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/integrations_check_complete.ts +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/integrations_check_complete.ts @@ -17,18 +17,37 @@ import type { IntegrationCardMetadata } from './types'; export const checkIntegrationsCardComplete: OnboardingCardCheckComplete< IntegrationCardMetadata > = async (services: StartServices) => { - const packageData = await services.http.get( - EPM_API_ROUTES.INSTALL_BY_UPLOAD_PATTERN, - { + const packageData = await services.http + .get(EPM_API_ROUTES.INSTALL_BY_UPLOAD_PATTERN, { version: '2023-10-31', - } - ); + }) + .catch((err: Error) => { + services.notifications.toasts.addError(err, { + title: i18n.translate( + 'xpack.securitySolution.onboarding.integrationsCard.checkComplete.fetchIntegrations.errorTitle', + { + defaultMessage: 'Error fetching integrations data', + } + ), + }); + return { items: [] }; + }); const agentsData = await lastValueFrom( services.data.search.search({ params: { index: AGENT_INDEX, body: { size: 1 } }, }) - ); + ).catch((err: Error) => { + services.notifications.toasts.addError(err, { + title: i18n.translate( + 'xpack.securitySolution.onboarding.integrationsCard.checkComplete.fetchAgents.errorTitle', + { + defaultMessage: 'Error fetching agents data', + } + ), + }); + return { rawResponse: { hits: { total: 0 } } }; + }); const installed = packageData?.items?.filter( (pkg) => diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/use_integration_card_list.test.ts b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/use_integration_card_list.test.ts index 9c4e1978f27b7..19ab340276b83 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/use_integration_card_list.test.ts +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/use_integration_card_list.test.ts @@ -6,12 +6,14 @@ */ import { renderHook } from '@testing-library/react-hooks'; import { useIntegrationCardList } from './use_integration_card_list'; +import { trackOnboardingLinkClick } from '../../../../common/lib/telemetry'; +jest.mock('../../../../common/lib/telemetry'); jest.mock('../../../../../common/lib/kibana', () => ({ ...jest.requireActual('../../../../../common/lib/kibana'), useNavigation: jest.fn().mockReturnValue({ navigateTo: jest.fn(), - getAppUrl: jest.fn(), + getAppUrl: jest.fn().mockReturnValue(''), }), })); @@ -73,4 +75,17 @@ describe('useIntegrationCardList', () => { expect(result.current).toEqual([mockFilteredCards.featuredCards['epr:endpoint']]); }); + + it('tracks integration card click', () => { + const { result } = renderHook(() => + useIntegrationCardList({ + integrationsList: mockIntegrationsList, + }) + ); + + const card = result.current[0]; + card.onCardClick?.(); + + expect(trackOnboardingLinkClick).toHaveBeenCalledWith('card_epr:endpoint'); + }); }); diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/use_integration_card_list.ts b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/use_integration_card_list.ts index 2a9675f91e9a8..ccea5299551c1 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/use_integration_card_list.ts +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/use_integration_card_list.ts @@ -20,8 +20,10 @@ import { MAX_CARD_HEIGHT_IN_PX, ONBOARDING_APP_ID, ONBOARDING_LINK, + TELEMETRY_INTEGRATION_CARD, } from './constants'; import type { GetAppUrl, NavigateTo } from '../../../../../common/lib/kibana'; +import { trackOnboardingLinkClick } from '../../../../common/lib/telemetry'; const addPathParamToUrl = (url: string, onboardingLink: string) => { const encoded = encodeURIComponent(onboardingLink); @@ -97,6 +99,8 @@ const addSecuritySpecificProps = ({ showInstallationStatus: true, url, onCardClick: () => { + const trackId = `${TELEMETRY_INTEGRATION_CARD}_${card.id}`; + trackOnboardingLinkClick(trackId); if (url.startsWith(APP_INTEGRATIONS_PATH)) { navigateTo({ appId: INTEGRATION_APP_ID, diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/hooks/use_completed_cards.test.ts b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/hooks/use_completed_cards.test.ts index 31c440e8f1415..2c9fcd573f0d6 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/hooks/use_completed_cards.test.ts +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/hooks/use_completed_cards.test.ts @@ -11,9 +11,11 @@ import { useCompletedCards } from './use_completed_cards'; import type { OnboardingGroupConfig } from '../../../types'; import type { OnboardingCardId } from '../../../constants'; import { mockReportCardComplete } from '../../__mocks__/onboarding_context_mocks'; +import { useKibana } from '../../../../common/lib/kibana'; const defaultStoredCompletedCardIds: OnboardingCardId[] = []; const mockSetStoredCompletedCardIds = jest.fn(); +const mockUseKibana = useKibana as jest.Mock; const mockUseStoredCompletedCardIds = jest.fn(() => [ defaultStoredCompletedCardIds, mockSetStoredCompletedCardIds, @@ -24,6 +26,15 @@ jest.mock('../../../hooks/use_stored_state', () => ({ })); jest.mock('../../onboarding_context'); +jest.mock('../../../../common/lib/kibana', () => { + const original = jest.requireActual('../../../../common/lib/kibana'); + return { + ...original, + useKibana: jest.fn().mockReturnValue({ + services: { notifications: { toasts: { addError: jest.fn() } } }, + }), + }; +}); const cardComplete = { id: 'card-completed' as OnboardingCardId, @@ -62,6 +73,13 @@ const cardMetadata = { .fn() .mockResolvedValue({ isComplete: true, metadata: { custom: 'metadata' } }), }; +const mockAddError = jest.fn(); +const mockError = new Error('Failed to check complete'); +const cardCheckCompleteFailed = { + id: 'card-failed' as OnboardingCardId, + title: 'card failed', + checkComplete: jest.fn().mockRejectedValue(mockError), +}; const mockCardsGroupConfig = [ { @@ -74,11 +92,65 @@ const mockCardsGroupConfig = [ }, ] as unknown as OnboardingGroupConfig[]; +const mockFailureCardsGroupConfig = [ + { + title: 'Group 1', + cards: [cardCheckCompleteFailed], + }, +] as unknown as OnboardingGroupConfig[]; + describe('useCompletedCards Hook', () => { beforeEach(() => { jest.clearAllMocks(); }); + describe('when checkComplete functions are rejected', () => { + let renderResult: RenderHookResult< + OnboardingGroupConfig[], + ReturnType + >; + beforeEach(async () => { + mockUseKibana.mockReturnValue({ + services: { notifications: { toasts: { addError: mockAddError } } }, + }); + renderResult = renderHook(useCompletedCards, { initialProps: mockFailureCardsGroupConfig }); + await act(async () => { + await waitFor(() => { + expect(mockSetStoredCompletedCardIds).toHaveBeenCalledTimes(0); // number of completed cards + }); + }); + }); + + describe('when a the auto check is called', () => { + beforeEach(async () => { + jest.clearAllMocks(); + await act(async () => { + renderResult.result.current.checkCardComplete(cardCheckCompleteFailed.id); + }); + }); + + it('should not set the completed card ids', async () => { + expect(mockSetStoredCompletedCardIds).not.toHaveBeenCalled(); + }); + + it('should return the correct completed state', () => { + expect(renderResult.result.current.isCardComplete(cardCheckCompleteFailed.id)).toEqual( + false + ); + }); + + it('should show an error toast', () => { + expect(mockAddError).toHaveBeenCalledWith(mockError, { + title: cardCheckCompleteFailed.title, + }); + }); + + it('should not report the completed card', async () => { + expect(mockReportCardComplete).not.toHaveBeenCalled(); + }); + }); + }); + describe('when checkComplete functions are resolved', () => { let renderResult: RenderHookResult< OnboardingGroupConfig[], diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/hooks/use_completed_cards.ts b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/hooks/use_completed_cards.ts index 6d8b22c504be9..34092bf2d5eec 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/hooks/use_completed_cards.ts +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/hooks/use_completed_cards.ts @@ -114,9 +114,17 @@ export const useCompletedCards = (cardsGroupConfig: OnboardingGroupConfig[]) => const cardConfig = cardsWithAutoCheck.find(({ id }) => id === cardId); if (cardConfig) { - cardConfig.checkComplete?.(services).then((checkCompleteResult) => { - processCardCheckCompleteResult(cardId, checkCompleteResult); - }); + cardConfig + .checkComplete?.(services) + .catch((err: Error) => { + services.notifications.toasts.addError(err, { title: cardConfig.title }); + return { + isComplete: false, + }; + }) + .then((checkCompleteResult) => { + processCardCheckCompleteResult(cardId, checkCompleteResult); + }); } }, [cardsWithAutoCheck, processCardCheckCompleteResult, services] @@ -129,9 +137,17 @@ export const useCompletedCards = (cardsGroupConfig: OnboardingGroupConfig[]) => } autoCheckCompletedRef.current = true; cardsWithAutoCheck.map((card) => - card.checkComplete?.(services).then((checkCompleteResult) => { - processCardCheckCompleteResult(card.id, checkCompleteResult); - }) + card + .checkComplete?.(services) + .catch((err: Error) => { + services.notifications.toasts.addError(err, { title: card.title }); + return { + isComplete: false, + }; + }) + .then((checkCompleteResult) => { + processCardCheckCompleteResult(card.id, checkCompleteResult); + }) ); }, [cardsWithAutoCheck, processCardCheckCompleteResult, services]); diff --git a/x-pack/plugins/security_solution/public/timelines/components/notes/save_timeline.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/notes/save_timeline.test.tsx index 36a45e993674f..8a024aec79840 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/notes/save_timeline.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/notes/save_timeline.test.tsx @@ -42,11 +42,11 @@ describe('SaveTimelineCallout', () => { expect(getByTestId(SAVE_TIMELINE_BUTTON_TEST_ID)).toBeInTheDocument(); expect(getByTestId(SAVE_TIMELINE_BUTTON_TEST_ID)).toHaveStyle('background-color: #BD271E'); - expect(getByTestId(SAVE_TIMELINE_BUTTON_TEST_ID)).toHaveTextContent('Save timeline'); + expect(getByTestId(SAVE_TIMELINE_BUTTON_TEST_ID)).toHaveTextContent('Save Timeline'); expect(getByTestId(SAVE_TIMELINE_CALLOUT_TEST_ID)).toBeInTheDocument(); - expect(getAllByText('Save timeline')).toHaveLength(2); + expect(getAllByText('Save Timeline')).toHaveLength(2); expect( - getByText('You need to save your timeline before creating notes for it.') + getByText('You must save this Timeline before attaching notes to it.') ).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/notes/save_timeline.tsx b/x-pack/plugins/security_solution/public/timelines/components/notes/save_timeline.tsx index 0c0a56f2699d3..12efd2db689b7 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/notes/save_timeline.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/notes/save_timeline.tsx @@ -16,19 +16,19 @@ import { SaveTimelineButton } from '../modal/actions/save_timeline_button'; export const SAVE_TIMELINE_CALLOUT_TITLE = i18n.translate( 'xpack.securitySolution.timeline.notes.saveTimeline.calloutTitle', { - defaultMessage: 'Save timeline', + defaultMessage: 'Save Timeline', } ); export const SAVE_TIMELINE_CALLOUT_CONTENT = i18n.translate( 'xpack.securitySolution.timeline.notes.saveTimeline.calloutContent', { - defaultMessage: 'You need to save your timeline before creating notes for it.', + defaultMessage: 'You must save this Timeline before attaching notes to it.', } ); export const SAVE_TIMELINE_BUTTON = i18n.translate( - 'xpack.securitySolution.flyout.left.notes.savedTimelineButtonLabel', + 'xpack.securitySolution.flyout.left.notes.saveTimeline.buttonLabel', { - defaultMessage: 'Save timeline', + defaultMessage: 'Save Timeline', } ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/notes/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/notes/index.tsx index 737001125c990..aad74f15d4f62 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/notes/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/notes/index.tsx @@ -59,7 +59,7 @@ export const FETCH_NOTES_ERROR = i18n.translate( } ); export const NO_NOTES = i18n.translate('xpack.securitySolution.notes.noNotesLabel', { - defaultMessage: 'No notes have yet been created for this timeline', + defaultMessage: 'No notes have been created for this Timeline.', }); interface NotesTabContentProps { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/query_tab_unified_components.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/query_tab_unified_components.test.tsx index 9d450f46f4b01..107c166183647 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/query_tab_unified_components.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/query_tab_unified_components.test.tsx @@ -918,7 +918,7 @@ describe('query tab with unified timeline', () => { await waitFor(() => { expect(screen.getByTestId('timeline-notes-tool-tip')).toBeInTheDocument(); expect(screen.getByTestId('timeline-notes-tool-tip')).toHaveTextContent( - '1 Note available. Click to view it & add more.' + '1 note available. Click to view it and add more.' ); }); }, @@ -975,7 +975,7 @@ describe('query tab with unified timeline', () => { await waitFor(() => { expect(screen.getByTestId('timeline-notes-tool-tip')).toBeVisible(); expect(screen.getByTestId('timeline-notes-tool-tip')).toHaveTextContent( - '1 Note available. Click to view it & add more.' + '1 note available. Click to view it and add more.' ); }); }, diff --git a/x-pack/plugins/security_solution/scripts/endpoint/agent_downloader_cli/agent_downloader.test.ts b/x-pack/plugins/security_solution/scripts/endpoint/agent_downloader_cli/agent_downloader.test.ts new file mode 100644 index 0000000000000..a39ad186e62b6 --- /dev/null +++ b/x-pack/plugins/security_solution/scripts/endpoint/agent_downloader_cli/agent_downloader.test.ts @@ -0,0 +1,203 @@ +/* + * Copyright 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 { getAgentDownloadUrl, getAgentFileName } from '../common/fleet_services'; +import { downloadAndStoreAgent } from '../common/agent_downloads_service'; +import type { ToolingLog } from '@kbn/tooling-log'; +import { agentDownloaderRunner } from './agent_downloader'; +import type { RunContext } from '@kbn/dev-cli-runner'; + +jest.mock('../common/fleet_services'); +jest.mock('../common/agent_downloads_service'); + +describe('agentDownloaderRunner', () => { + let log: ToolingLog; + + beforeEach(() => { + log = { + info: jest.fn(), + error: jest.fn(), + } as unknown as ToolingLog; + + jest.clearAllMocks(); + }); + + const version = '8.15.0'; + let closestMatch = false; + const url = 'http://example.com/agent.tar.gz'; + const fileName = 'elastic-agent-8.15.0.tar.gz'; + + it('downloads and stores the specified version', async () => { + (getAgentDownloadUrl as jest.Mock).mockResolvedValue({ url }); + (getAgentFileName as jest.Mock).mockReturnValue('elastic-agent-8.15.0'); + (downloadAndStoreAgent as jest.Mock).mockResolvedValue(undefined); + + await agentDownloaderRunner({ + flags: { version, closestMatch }, + log, + } as unknown as RunContext); + + expect(getAgentDownloadUrl).toHaveBeenCalledWith(version, closestMatch, log); + expect(getAgentFileName).toHaveBeenCalledWith(version); + expect(downloadAndStoreAgent).toHaveBeenCalledWith(url, fileName); + expect(log.info).toHaveBeenCalledWith('Successfully downloaded and stored version 8.15.0'); + }); + + it('logs an error if the download fails', async () => { + (getAgentDownloadUrl as jest.Mock).mockResolvedValue({ url }); + (getAgentFileName as jest.Mock).mockReturnValue('elastic-agent-8.15.0'); + (downloadAndStoreAgent as jest.Mock).mockRejectedValue(new Error('Download failed')); + + await agentDownloaderRunner({ + flags: { version, closestMatch }, + log, + } as unknown as RunContext); + + expect(getAgentDownloadUrl).toHaveBeenCalledWith(version, closestMatch, log); + expect(getAgentFileName).toHaveBeenCalledWith(version); + expect(downloadAndStoreAgent).toHaveBeenCalledWith(url, fileName); + expect(log.error).toHaveBeenCalledWith( + 'Failed to download or store version 8.15.0: Download failed' + ); + }); + + it('downloads and stores the previous patch version if the specified version fails', async () => { + const fallbackVersion = '8.15.0'; + const fallbackFileName = 'elastic-agent-8.15.0.tar.gz'; + + (getAgentDownloadUrl as jest.Mock) + .mockResolvedValueOnce({ url }) + .mockResolvedValueOnce({ url }); + (getAgentFileName as jest.Mock) + .mockReturnValueOnce('elastic-agent-8.15.1') + .mockReturnValueOnce('elastic-agent-8.15.0'); + (downloadAndStoreAgent as jest.Mock) + .mockRejectedValueOnce(new Error('Download failed')) + .mockResolvedValueOnce(undefined); + + await agentDownloaderRunner({ + flags: { version: '8.15.1', closestMatch }, + log, + } as unknown as RunContext); + + expect(getAgentDownloadUrl).toHaveBeenCalledWith('8.15.1', closestMatch, log); + expect(getAgentDownloadUrl).toHaveBeenCalledWith(fallbackVersion, closestMatch, log); + expect(getAgentFileName).toHaveBeenCalledWith('8.15.1'); + expect(getAgentFileName).toHaveBeenCalledWith(fallbackVersion); + expect(downloadAndStoreAgent).toHaveBeenCalledWith(url, 'elastic-agent-8.15.1.tar.gz'); + expect(downloadAndStoreAgent).toHaveBeenCalledWith(url, fallbackFileName); + expect(log.error).toHaveBeenCalledWith( + 'Failed to download or store version 8.15.1: Download failed' + ); + expect(log.info).toHaveBeenCalledWith('Successfully downloaded and stored version 8.15.0'); + }); + + it('logs an error if all downloads fail', async () => { + (getAgentDownloadUrl as jest.Mock).mockResolvedValue({ url }); + (getAgentFileName as jest.Mock) + .mockReturnValueOnce('elastic-agent-8.15.1') + .mockReturnValueOnce('elastic-agent-8.15.0'); + (downloadAndStoreAgent as jest.Mock) + .mockRejectedValueOnce(new Error('Download failed')) + .mockRejectedValueOnce(new Error('Download failed')); + + await agentDownloaderRunner({ + flags: { version: '8.15.1', closestMatch }, + log, + } as unknown as RunContext); + + expect(getAgentDownloadUrl).toHaveBeenCalledWith('8.15.1', closestMatch, log); + expect(getAgentDownloadUrl).toHaveBeenCalledWith('8.15.0', closestMatch, log); + expect(getAgentFileName).toHaveBeenCalledWith('8.15.1'); + expect(getAgentFileName).toHaveBeenCalledWith('8.15.0'); + expect(downloadAndStoreAgent).toHaveBeenCalledWith(url, 'elastic-agent-8.15.1.tar.gz'); + expect(downloadAndStoreAgent).toHaveBeenCalledWith(url, 'elastic-agent-8.15.0.tar.gz'); + expect(log.error).toHaveBeenCalledWith( + 'Failed to download or store version 8.15.1: Download failed' + ); + expect(log.error).toHaveBeenCalledWith( + 'Failed to download or store version 8.15.0: Download failed' + ); + }); + + it('does not attempt fallback when patch version is 0', async () => { + (getAgentDownloadUrl as jest.Mock).mockResolvedValue({ url }); + (getAgentFileName as jest.Mock).mockReturnValue('elastic-agent-8.15.0'); + (downloadAndStoreAgent as jest.Mock).mockResolvedValue(undefined); + + await agentDownloaderRunner({ + flags: { version: '8.15.0', closestMatch }, + log, + } as unknown as RunContext); + + expect(getAgentDownloadUrl).toHaveBeenCalledTimes(1); // Only one call for 8.15.0 + expect(getAgentFileName).toHaveBeenCalledTimes(1); + expect(downloadAndStoreAgent).toHaveBeenCalledWith(url, fileName); + expect(log.info).toHaveBeenCalledWith('Successfully downloaded and stored version 8.15.0'); + }); + + it('logs an error for an invalid version format', async () => { + const invalidVersion = '7.x.x'; + + await expect( + agentDownloaderRunner({ + flags: { version: invalidVersion, closestMatch }, + log, + } as unknown as RunContext) + ).rejects.toThrow('Invalid version format'); + }); + + it('passes the closestMatch flag correctly', async () => { + closestMatch = true; + + (getAgentDownloadUrl as jest.Mock).mockResolvedValue({ url }); + (getAgentFileName as jest.Mock).mockReturnValue('elastic-agent-8.15.0'); + (downloadAndStoreAgent as jest.Mock).mockResolvedValue(undefined); + + await agentDownloaderRunner({ + flags: { version, closestMatch }, + log, + } as unknown as RunContext); + + expect(getAgentDownloadUrl).toHaveBeenCalledWith(version, closestMatch, log); + }); + + it('throws an error when version is not provided', async () => { + await expect( + agentDownloaderRunner({ + flags: { closestMatch }, + log, + } as unknown as RunContext) + ).rejects.toThrow('version argument is required'); + }); + + it('logs the correct messages when both version and fallback version are processed', async () => { + const primaryVersion = '8.15.1'; + + (getAgentDownloadUrl as jest.Mock) + .mockResolvedValueOnce({ url }) + .mockResolvedValueOnce({ url }); + + (getAgentFileName as jest.Mock) + .mockReturnValueOnce('elastic-agent-8.15.1') + .mockReturnValueOnce('elastic-agent-8.15.0'); + + (downloadAndStoreAgent as jest.Mock) + .mockRejectedValueOnce(new Error('Download failed')) // Fail on primary + .mockResolvedValueOnce(undefined); // Success on fallback + + await agentDownloaderRunner({ + flags: { version: primaryVersion, closestMatch }, + log, + } as unknown as RunContext); + + expect(log.error).toHaveBeenCalledWith( + 'Failed to download or store version 8.15.1: Download failed' + ); + expect(log.info).toHaveBeenCalledWith('Successfully downloaded and stored version 8.15.0'); + }); +}); diff --git a/x-pack/plugins/security_solution/scripts/endpoint/agent_downloader_cli/agent_downloader.ts b/x-pack/plugins/security_solution/scripts/endpoint/agent_downloader_cli/agent_downloader.ts index ab1da6a3f208f..8366c77575e70 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/agent_downloader_cli/agent_downloader.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/agent_downloader_cli/agent_downloader.ts @@ -8,24 +8,72 @@ import { ok } from 'assert'; import type { RunFn } from '@kbn/dev-cli-runner'; import type { ToolingLog } from '@kbn/tooling-log'; +import semver from 'semver'; import { getAgentDownloadUrl, getAgentFileName } from '../common/fleet_services'; import { downloadAndStoreAgent } from '../common/agent_downloads_service'; +// Decrement the patch version by 1 and preserve pre-release tag (if any) +const decrementPatchVersion = (version: string): string | null => { + const parsedVersion = semver.parse(version); + if (!parsedVersion) { + return null; + } + const newPatchVersion = parsedVersion.patch - 1; + // Create a new version string with the decremented patch - removing any possible pre-release tag + const newVersion = `${parsedVersion.major}.${parsedVersion.minor}.${newPatchVersion}`; + return semver.valid(newVersion) ? newVersion : null; +}; + +// Generate a list of versions to attempt downloading, including a fallback to the previous patch (GA) +const getVersionsToDownload = (version: string): string[] => { + const parsedVersion = semver.parse(version); + if (!parsedVersion) return []; + // If patch version is 0, return only the current version. + if (parsedVersion.patch === 0) { + return [version]; + } + + const decrementedVersion = decrementPatchVersion(version); + return decrementedVersion ? [version, decrementedVersion] : [version]; +}; + +// Download and store the Elastic Agent for the specified version(s) const downloadAndStoreElasticAgent = async ( version: string, closestMatch: boolean, log: ToolingLog -) => { - const downloadUrlResponse = await getAgentDownloadUrl(version, closestMatch, log); - const fileNameNoExtension = getAgentFileName(version); - const agentFile = `${fileNameNoExtension}.tar.gz`; - await downloadAndStoreAgent(downloadUrlResponse.url, agentFile); +): Promise => { + const versionsToDownload = getVersionsToDownload(version); + + // Although we have a list of versions to try downloading, we only need to download one, and will return as soon as it succeeds. + for (const versionToDownload of versionsToDownload) { + try { + const { url } = await getAgentDownloadUrl(versionToDownload, closestMatch, log); + const fileName = `${getAgentFileName(versionToDownload)}.tar.gz`; + + await downloadAndStoreAgent(url, fileName); + log.info(`Successfully downloaded and stored version ${versionToDownload}`); + return; // Exit once successful + } catch (error) { + log.error(`Failed to download or store version ${versionToDownload}: ${error.message}`); + } + } + + log.error(`Failed to download agent for any available version: ${versionsToDownload.join(', ')}`); }; export const agentDownloaderRunner: RunFn = async (cliContext) => { - ok(cliContext.flags.version, 'version argument is required'); + const { version } = cliContext.flags; + + ok(version, 'version argument is required'); + + // Validate version format + if (!semver.valid(version as string)) { + throw new Error('Invalid version format'); + } + await downloadAndStoreElasticAgent( - cliContext.flags.version as string, + version as string, cliContext.flags.closestMatch as boolean, cliContext.log ); diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/agent_downloads_service.test.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/agent_downloads_service.test.ts new file mode 100644 index 0000000000000..0a7a9d3104798 --- /dev/null +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/agent_downloads_service.test.ts @@ -0,0 +1,77 @@ +/* + * Copyright 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. + */ +// Adjust path if needed + +import { downloadAndStoreAgent, isAgentDownloadFromDiskAvailable } from './agent_downloads_service'; +import fs from 'fs'; +import nodeFetch from 'node-fetch'; +import { finished } from 'stream/promises'; + +jest.mock('fs'); +jest.mock('node-fetch'); +jest.mock('stream/promises', () => ({ + finished: jest.fn(), +})); +jest.mock('../../../common/endpoint/data_loaders/utils', () => ({ + createToolingLogger: jest.fn(() => ({ + debug: jest.fn(), + info: jest.fn(), + error: jest.fn(), + })), +})); + +describe('AgentDownloadStorage', () => { + const url = 'http://example.com/agent.tar.gz'; + const fileName = 'elastic-agent-7.10.0.tar.gz'; + beforeEach(() => { + jest.clearAllMocks(); // Ensure no previous test state affects the current one + }); + + it('downloads and stores the agent if not cached', async () => { + (fs.existsSync as unknown as jest.Mock).mockReturnValue(false); + (fs.createWriteStream as unknown as jest.Mock).mockReturnValue({ + on: jest.fn(), + end: jest.fn(), + }); + (nodeFetch as unknown as jest.Mock).mockResolvedValue({ body: { pipe: jest.fn() } }); + (finished as unknown as jest.Mock).mockResolvedValue(undefined); + + const result = await downloadAndStoreAgent(url, fileName); + + expect(result).toEqual({ + url, + filename: fileName, + directory: expect.any(String), + fullFilePath: expect.stringContaining(fileName), // Dynamically match the file path + }); + }); + + it('reuses cached agent if available', async () => { + (fs.existsSync as unknown as jest.Mock).mockReturnValue(true); + + const result = await downloadAndStoreAgent(url, fileName); + + expect(result).toEqual({ + url, + filename: fileName, + directory: expect.any(String), + fullFilePath: expect.stringContaining(fileName), // Dynamically match the path + }); + }); + + it('checks if agent download is available from disk', () => { + (fs.existsSync as unknown as jest.Mock).mockReturnValue(true); + + const result = isAgentDownloadFromDiskAvailable(fileName); + + expect(result).toEqual({ + filename: fileName, + directory: expect.any(String), + fullFilePath: expect.stringContaining(fileName), // Dynamically match the path + }); + }); +}); diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/agent_downloads_service.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/agent_downloads_service.ts index 488e1b10160e8..4c963332ad0c2 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/agent_downloads_service.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/agent_downloads_service.ts @@ -5,6 +5,7 @@ * 2.0. */ +import pRetry from 'p-retry'; import { mkdir, readdir, stat, unlink } from 'fs/promises'; import { join } from 'path'; import fs from 'fs'; @@ -24,7 +25,7 @@ export interface DownloadedAgentInfo { interface AgentDownloadStorageSettings { /** - * Last time a cleanup was ran. Date in ISO format + * Last time a cleanup was performed. Date in ISO format */ lastCleanup: string; @@ -47,7 +48,7 @@ class AgentDownloadStorage extends SettingsStorage constructor() { super('agent_download_storage_settings.json', { defaultSettings: { - maxFileAge: 1.728e8, // 2 days + maxFileAge: 1.728e8, // 2 days in milliseconds lastCleanup: new Date().toISOString(), }, }); @@ -55,20 +56,25 @@ class AgentDownloadStorage extends SettingsStorage this.downloadsDirFullPath = this.buildPath(this.downloadsDirName); } + /** + * Ensures the download directory exists on disk + */ protected async ensureExists(): Promise { await super.ensureExists(); if (!this.downloadsFolderExists) { await mkdir(this.downloadsDirFullPath, { recursive: true }); - this.log.debug(`Created directory [this.downloadsDirFullPath] for cached agent downloads`); + this.log.debug(`Created directory [${this.downloadsDirFullPath}] for cached agent downloads`); this.downloadsFolderExists = true; } } + /** + * Gets the file paths for a given download URL and optional file name. + */ public getPathsForUrl(agentDownloadUrl: string, agentFileName?: string): DownloadedAgentInfo { - const filename = agentFileName - ? agentFileName - : agentDownloadUrl.replace(/^https?:\/\//gi, '').replace(/\//g, '#'); + const filename = + agentFileName || agentDownloadUrl.replace(/^https?:\/\//gi, '').replace(/\//g, '#'); const directory = this.downloadsDirFullPath; const fullFilePath = this.buildPath(join(this.downloadsDirName, filename)); @@ -79,59 +85,67 @@ class AgentDownloadStorage extends SettingsStorage }; } + /** + * Downloads the agent and stores it locally. Reuses existing downloads if available. + */ public async downloadAndStore( agentDownloadUrl: string, agentFileName?: string ): Promise { - this.log.debug(`Downloading and storing: ${agentDownloadUrl}`); - - // TODO: should we add "retry" attempts to file downloads? + this.log.debug(`Starting download: ${agentDownloadUrl}`); await this.ensureExists(); - const newDownloadInfo = this.getPathsForUrl(agentDownloadUrl, agentFileName); - // If download is already present on disk, then just return that info. No need to re-download it + // Return cached version if the file already exists if (fs.existsSync(newDownloadInfo.fullFilePath)) { this.log.debug(`Download already cached at [${newDownloadInfo.fullFilePath}]`); return newDownloadInfo; } try { - const outputStream = fs.createWriteStream(newDownloadInfo.fullFilePath); - - await handleProcessInterruptions( - async () => { - const { body } = await nodeFetch(agentDownloadUrl); - await finished(body.pipe(outputStream)); + await pRetry( + async (attempt) => { + this.log.info( + `Attempt ${attempt} - Downloading agent from [${agentDownloadUrl}] to [${newDownloadInfo.fullFilePath}]` + ); + const outputStream = fs.createWriteStream(newDownloadInfo.fullFilePath); + + await handleProcessInterruptions( + async () => { + const { body } = await nodeFetch(agentDownloadUrl); + await finished(body.pipe(outputStream)); + }, + () => fs.unlinkSync(newDownloadInfo.fullFilePath) // Clean up on interruption + ); + this.log.info(`Successfully downloaded agent to [${newDownloadInfo.fullFilePath}]`); }, - () => { - fs.unlinkSync(newDownloadInfo.fullFilePath); + { + retries: 2, // 2 retries = 3 total attempts (1 initial + 2 retries) + onFailedAttempt: (error) => { + this.log.error(`Download attempt ${error.attemptNumber} failed: ${error.message}`); + // Cleanup failed download + return unlink(newDownloadInfo.fullFilePath); + }, } ); - } catch (e) { - // Try to clean up download case it failed halfway through - await unlink(newDownloadInfo.fullFilePath); - - throw e; + } catch (error) { + throw new Error(`Download failed after multiple attempts: ${error.message}`); } await this.cleanupDownloads(); - return newDownloadInfo; } public async cleanupDownloads(): Promise<{ deleted: string[] }> { - this.log.debug(`Performing cleanup of cached Agent downlaods`); + this.log.debug('Performing cleanup of cached Agent downloads'); const settings = await this.get(); - const maxAgeDate = new Date(); + const maxAgeDate = new Date(Date.now() - settings.maxFileAge); const response: { deleted: string[] } = { deleted: [] }; - maxAgeDate.setMilliseconds(settings.maxFileAge * -1); // `* -1` to set time back - - // If cleanup already happen within the file age, then nothing to do. Exit. if (settings.lastCleanup > maxAgeDate.toISOString()) { + this.log.debug('Skipping cleanup, as it was performed recently.'); return response; } @@ -140,41 +154,48 @@ class AgentDownloadStorage extends SettingsStorage lastCleanup: new Date().toISOString(), }); - const deleteFilePromises: Array> = []; - const allFiles = await readdir(this.downloadsDirFullPath); - - for (const fileName of allFiles) { - const filePath = join(this.downloadsDirFullPath, fileName); - const fileStats = await stat(filePath); + try { + const allFiles = await readdir(this.downloadsDirFullPath); + const deleteFilePromises = allFiles.map(async (fileName) => { + const filePath = join(this.downloadsDirFullPath, fileName); + const fileStats = await stat(filePath); + if (fileStats.isFile() && fileStats.birthtime < maxAgeDate) { + try { + await unlink(filePath); + response.deleted.push(filePath); + } catch (err) { + this.log.error(`Failed to delete file [${filePath}]: ${err.message}`); + } + } + }); - if (fileStats.isFile() && fileStats.birthtime < maxAgeDate) { - deleteFilePromises.push(unlink(filePath)); - response.deleted.push(filePath); - } + await Promise.allSettled(deleteFilePromises); + this.log.debug(`Deleted ${response.deleted.length} file(s)`); + return response; + } catch (err) { + this.log.error(`Error during cleanup: ${err.message}`); + return response; } - - await Promise.allSettled(deleteFilePromises); - - this.log.debug(`Deleted [${response.deleted.length}] file(s)`); - this.log.verbose(`files deleted:\n`, response.deleted.join('\n')); - - return response; } + /** + * Checks if a specific agent download is available locally. + */ public isAgentDownloadFromDiskAvailable(filename: string): DownloadedAgentInfo | undefined { - if (fs.existsSync(join(this.downloadsDirFullPath, filename))) { + const filePath = join(this.downloadsDirFullPath, filename); + if (fs.existsSync(filePath)) { return { filename, /** The local directory where downloads are stored */ directory: this.downloadsDirFullPath, /** The full local file path and name */ - fullFilePath: join(this.downloadsDirFullPath, filename), + fullFilePath: filePath, }; } } } -const agentDownloadsClient = new AgentDownloadStorage(); +export const agentDownloadsClient = new AgentDownloadStorage(); export interface DownloadAndStoreAgentResponse extends DownloadedAgentInfo { url: string; @@ -203,12 +224,15 @@ export const downloadAndStoreAgent = async ( }; /** - * Cleans up the old agent downloads on disk. + * Cleans up old agent downloads on disk. */ export const cleanupDownloads = async (): ReturnType => { return agentDownloadsClient.cleanupDownloads(); }; +/** + * Checks if a specific agent download is available from disk. + */ export const isAgentDownloadFromDiskAvailable = ( fileName: string ): DownloadedAgentInfo | undefined => { diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/constants.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/constants.ts index 4a8b2daca2af4..9ca18f0d68f2c 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/constants.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/constants.ts @@ -14,4 +14,5 @@ export const ENDPOINT_ALERTS_INDEX = 'logs-endpoint.alerts-default'; export const COMMON_API_HEADERS = Object.freeze({ 'kbn-xsrf': 'security-solution', 'x-elastic-internal-origin': 'security-solution', + 'elastic-api-version': '2023-10-31', }); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts index 9603e9e9a6d48..2f6e46d1d7727 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts @@ -95,6 +95,7 @@ export function registerEndpointRoutes( access: 'public', path: METADATA_TRANSFORMS_STATUS_ROUTE, options: { authRequired: true, tags: ['access:securitySolution'] }, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }) .addVersion( diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/suggestions/index.ts b/x-pack/plugins/security_solution/server/endpoint/routes/suggestions/index.ts index a4853d9772ad7..677fb004ee862 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/suggestions/index.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/suggestions/index.ts @@ -43,6 +43,7 @@ export function registerEndpointSuggestionsRoutes( access: 'public', path: SUGGESTIONS_ROUTE, options: { authRequired: true, tags: ['access:securitySolution'] }, + // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }) .addVersion( diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/elasticsearch_assets/enrich_policy.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/elasticsearch_assets/enrich_policy.ts index 7d6fc6fd8bc24..4b8ce594a6cb7 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/elasticsearch_assets/enrich_policy.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/elasticsearch_assets/enrich_policy.ts @@ -72,10 +72,36 @@ export const executeFieldRetentionEnrichPolicy = async ({ export const deleteFieldRetentionEnrichPolicy = async ({ unitedDefinition, esClient, + logger, + attempts = 5, + delayMs = 2000, }: { - esClient: ElasticsearchClient; unitedDefinition: DefinitionMetadata; + esClient: ElasticsearchClient; + logger: Logger; + attempts?: number; + delayMs?: number; }) => { const name = getFieldRetentionEnrichPolicyName(unitedDefinition); - return esClient.enrich.deletePolicy({ name }, { ignore: [404] }); + let currentAttempt = 1; + while (currentAttempt <= attempts) { + try { + await esClient.enrich.deletePolicy({ name }, { ignore: [404] }); + return; + } catch (e) { + // a 429 status code indicates that the enrich policy is being executed + if (currentAttempt === attempts || e.statusCode !== 429) { + logger.error( + `Error deleting enrich policy ${name}: ${e.message} after ${currentAttempt} attempts` + ); + throw e; + } + + logger.info( + `Enrich policy ${name} is being executed, waiting for it to finish before deleting` + ); + await new Promise((resolve) => setTimeout(resolve, delayMs)); + currentAttempt++; + } + } }; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.test.ts index 8079e54ac9ba6..858047952801d 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.test.ts @@ -39,23 +39,25 @@ describe('EntityStoreDataClient', () => { sortOrder: 'asc' as SortOrder, }; + const emptySearchResponse = { + took: 0, + timed_out: false, + _shards: { + total: 0, + successful: 0, + skipped: 0, + failed: 0, + }, + hits: { + total: 0, + hits: [], + }, + }; + describe('search entities', () => { beforeEach(() => { jest.resetAllMocks(); - esClientMock.search.mockResolvedValue({ - took: 0, - timed_out: false, - _shards: { - total: 0, - successful: 0, - skipped: 0, - failed: 0, - }, - hits: { - total: 0, - hits: [], - }, - }); + esClientMock.search.mockResolvedValue(emptySearchResponse); }); it('searches in the entities store indices', async () => { @@ -133,5 +135,47 @@ describe('EntityStoreDataClient', () => { expect(response.inspect).toMatchSnapshot(); }); + + it('returns searched entity record', async () => { + const fakeEntityRecord = { entity_record: true, asset: { criticality: 'low' } }; + + esClientMock.search.mockResolvedValue({ + ...emptySearchResponse, + hits: { + total: 1, + hits: [ + { + _index: '.entities.v1.latest.security_host_default', + _source: fakeEntityRecord, + }, + ], + }, + }); + + const response = await dataClient.searchEntities(defaultSearchParams); + + expect(response.records[0]).toEqual(fakeEntityRecord); + }); + + it("returns empty asset criticality when criticality value is 'deleted'", async () => { + const fakeEntityRecord = { entity_record: true }; + + esClientMock.search.mockResolvedValue({ + ...emptySearchResponse, + hits: { + total: 1, + hits: [ + { + _index: '.entities.v1.latest.security_host_default', + _source: { asset: { criticality: 'deleted' }, ...fakeEntityRecord }, + }, + ], + }, + }); + + const response = await dataClient.searchEntities(defaultSearchParams); + + expect(response.records[0]).toEqual(fakeEntityRecord); + }); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.ts index 5b1acaa433cd0..2cb119e6d37fe 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.ts @@ -53,6 +53,8 @@ import { isPromiseFulfilled, isPromiseRejected, } from './utils'; +import type { EntityRecord } from './types'; +import { CRITICALITY_VALUES } from '../asset_criticality/constants'; interface EntityStoreClientOpts { logger: Logger; @@ -245,7 +247,7 @@ export class EntityStoreDataClient { logger, taskManager, }); - logger.info(`Entity store initialized`); + logger.info(`Entity store initialized for ${entityType}`); return updated; } catch (err) { @@ -362,6 +364,7 @@ export class EntityStoreDataClient { await deleteFieldRetentionEnrichPolicy({ unitedDefinition, esClient: this.esClient, + logger, }); if (deleteData) { @@ -406,7 +409,7 @@ export class EntityStoreDataClient { const sort = sortField ? [{ [sortField]: sortOrder }] : undefined; const query = filterQuery ? JSON.parse(filterQuery) : undefined; - const response = await this.esClient.search({ + const response = await this.esClient.search({ index, query, size: Math.min(perPage, MAX_SEARCH_RESPONSE_SIZE), @@ -418,7 +421,19 @@ export class EntityStoreDataClient { const total = typeof hits.total === 'number' ? hits.total : hits.total?.value ?? 0; - const records = hits.hits.map((hit) => hit._source as Entity); + const records = hits.hits.map((hit) => { + const { asset, ...source } = hit._source as EntityRecord; + + const assetOverwrite: Pick = + asset && asset.criticality !== CRITICALITY_VALUES.DELETED + ? { asset: { criticality: asset.criticality } } + : {}; + + return { + ...source, + ...assetOverwrite, + }; + }); const inspect: InspectQuery = { dsl: [JSON.stringify({ index, body: query }, null, 2)], @@ -450,7 +465,7 @@ export class EntityStoreDataClient { originalStatus === ENGINE_STATUS.UPDATING ) { throw new Error( - `Error updating entity store: There is an changes already in progress for engine ${id}` + `Error updating entity store: There are changes already in progress for engine ${id}` ); } diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/types.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/types.ts new file mode 100644 index 0000000000000..e5f1e6db36bca --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/types.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 type { HostEntity, UserEntity } from '../../../../common/api/entity_analytics'; +import type { CriticalityValues } from '../asset_criticality/constants'; + +export interface HostEntityRecord extends Omit { + asset?: { + criticality: CriticalityValues; + }; +} + +export interface UserEntityRecord extends Omit { + asset?: { + criticality: CriticalityValues; + }; +} + +/** + * It represents the data stored in the entity store index. + */ +export type EntityRecord = HostEntityRecord | UserEntityRecord; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/entity_types/common.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/entity_types/common.ts index 2f0213d5f3820..ac974bf119d4a 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/entity_types/common.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/entity_types/common.ts @@ -7,7 +7,7 @@ import type { EntityType } from '../../../../../../common/api/entity_analytics/entity_store'; import { getIdentityFieldForEntityType } from '../../utils'; -import { collectValues, newestValue } from '../definition_utils'; +import { oldestValue, newestValue } from '../definition_utils'; import type { UnitedDefinitionField } from '../types'; export const getCommonUnitedFieldDefinitions = ({ @@ -19,10 +19,9 @@ export const getCommonUnitedFieldDefinitions = ({ }): UnitedDefinitionField[] => { const identityField = getIdentityFieldForEntityType(entityType); return [ - collectValues({ + oldestValue({ sourceField: '_index', field: 'entity.source', - fieldHistoryLength, }), newestValue({ field: 'asset.criticality' }), newestValue({ diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/get_united_definition.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/get_united_definition.test.ts index 2657917d45a78..81a381bc91873 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/get_united_definition.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/get_united_definition.test.ts @@ -117,8 +117,7 @@ describe('getUnitedEntityDefinition', () => { }, Object { "field": "entity.source", - "maxLength": 10, - "operation": "collect_values", + "operation": "prefer_oldest_value", }, Object { "field": "asset.criticality", @@ -219,8 +218,10 @@ describe('getUnitedEntityDefinition', () => { }, Object { "aggregation": Object { - "limit": 10, - "type": "terms", + "sort": Object { + "@timestamp": "asc", + }, + "type": "top_value", }, "destination": "entity.source", "source": "_index", @@ -373,8 +374,7 @@ describe('getUnitedEntityDefinition', () => { }, Object { "field": "entity.source", - "maxLength": 10, - "operation": "collect_values", + "operation": "prefer_oldest_value", }, Object { "field": "asset.criticality", @@ -467,8 +467,10 @@ describe('getUnitedEntityDefinition', () => { }, Object { "aggregation": Object { - "limit": 10, - "type": "terms", + "sort": Object { + "@timestamp": "asc", + }, + "type": "top_value", }, "destination": "entity.source", "source": "_index", diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/configurations.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/configurations.ts index 6dbf68c699fd5..610ada87f5159 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/configurations.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/configurations.ts @@ -129,6 +129,9 @@ export const riskScoreFieldMap: FieldMap = { } as const; export const mappingComponentName = '.risk-score-mappings'; +export const nameSpaceAwareMappingsComponentName = (namespace: string): string => { + return `${mappingComponentName}-${namespace}`; +}; export const totalFieldsLimit = 1000; export const getIndexPatternDataStream = (namespace: string): IIndexPatternString => ({ diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.test.ts index 2ddd04a766944..cec3330af52e6 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.test.ts @@ -40,6 +40,7 @@ jest.spyOn(transforms, 'scheduleTransformNow').mockResolvedValue(Promise.resolve describe('RiskScoreDataClient', () => { let riskScoreDataClient: RiskScoreDataClient; + let riskScoreDataClientWithNameSpace: RiskScoreDataClient; let mockSavedObjectClient: ReturnType; let logger: ReturnType; const esClient = elasticsearchServiceMock.createScopedClusterClient().asCurrentUser; @@ -56,6 +57,8 @@ describe('RiskScoreDataClient', () => { namespace: 'default', }; riskScoreDataClient = new RiskScoreDataClient(options); + const optionsWithNamespace = { ...options, namespace: 'space-1' }; + riskScoreDataClientWithNameSpace = new RiskScoreDataClient(optionsWithNamespace); }); afterEach(() => { @@ -80,369 +83,396 @@ describe('RiskScoreDataClient', () => { }); describe('init success', () => { - it('should initialize risk engine resources', async () => { - await riskScoreDataClient.init(); + it('should initialize risk engine resources in the appropriate space', async () => { + const assertComponentTemplate = (namespace: string) => { + expect(createOrUpdateComponentTemplate).toHaveBeenCalledWith( + expect.objectContaining({ + logger, + esClient, + template: expect.objectContaining({ + name: `.risk-score-mappings-${namespace}`, + _meta: { + managed: true, + }, + }), + totalFieldsLimit: 1000, + }) + ); + }; - expect(createOrUpdateComponentTemplate).toHaveBeenCalledWith( - expect.objectContaining({ + const assertIndexTemplate = (namespace: string) => { + expect(createOrUpdateIndexTemplate).toHaveBeenCalledWith({ logger, esClient, - template: expect.objectContaining({ - name: '.risk-score-mappings', - _meta: { - managed: true, - }, - }), - totalFieldsLimit: 1000, - }) - ); - expect((createOrUpdateComponentTemplate as jest.Mock).mock.lastCall[0].template.template) - .toMatchInlineSnapshot(` - Object { - "mappings": Object { - "dynamic": "strict", - "properties": Object { - "@timestamp": Object { - "ignore_malformed": false, - "type": "date", + template: { + name: `.risk-score.risk-score-${namespace}-index-template`, + body: { + data_stream: { hidden: true }, + index_patterns: [`risk-score.risk-score-${namespace}`], + composed_of: [`.risk-score-mappings-${namespace}`], + template: { + lifecycle: {}, + settings: { + 'index.mapping.total_fields.limit': totalFieldsLimit, + }, + mappings: { + dynamic: false, + _meta: { + kibana: { + version: '8.9.0', + }, + managed: true, + namespace, }, - "host": Object { - "properties": Object { - "name": Object { - "type": "keyword", - }, - "risk": Object { - "properties": Object { - "calculated_level": Object { - "type": "keyword", - }, - "calculated_score": Object { - "type": "float", - }, - "calculated_score_norm": Object { - "type": "float", - }, - "category_1_count": Object { - "type": "long", - }, - "category_1_score": Object { - "type": "float", - }, - "id_field": Object { - "type": "keyword", - }, - "id_value": Object { - "type": "keyword", - }, - "inputs": Object { - "properties": Object { - "category": Object { - "type": "keyword", - }, - "description": Object { - "type": "keyword", - }, - "id": Object { - "type": "keyword", - }, - "index": Object { - "type": "keyword", - }, - "risk_score": Object { - "type": "float", - }, - "timestamp": Object { - "type": "date", - }, + }, + }, + _meta: { + kibana: { + version: '8.9.0', + }, + managed: true, + namespace, + }, + }, + }, + }); + }; + + const assertDataStream = (namespace: string) => { + expect(createDataStream).toHaveBeenCalledWith({ + logger, + esClient, + totalFieldsLimit, + indexPatterns: { + template: `.risk-score.risk-score-${namespace}-index-template`, + alias: `risk-score.risk-score-${namespace}`, + }, + }); + }; + + const assertIndex = (namespace: string) => { + expect(createOrUpdateIndex).toHaveBeenCalledWith({ + logger, + esClient, + options: { + index: `risk-score.risk-score-latest-${namespace}`, + mappings: { + dynamic: false, + properties: { + '@timestamp': { + ignore_malformed: false, + type: 'date', + }, + host: { + properties: { + name: { + type: 'keyword', + }, + risk: { + properties: { + calculated_level: { + type: 'keyword', + }, + calculated_score: { + type: 'float', + }, + calculated_score_norm: { + type: 'float', + }, + category_1_count: { + type: 'long', + }, + category_1_score: { + type: 'float', + }, + id_field: { + type: 'keyword', + }, + id_value: { + type: 'keyword', + }, + inputs: { + properties: { + category: { + type: 'keyword', + }, + description: { + type: 'keyword', + }, + id: { + type: 'keyword', + }, + index: { + type: 'keyword', + }, + risk_score: { + type: 'float', + }, + timestamp: { + type: 'date', }, - "type": "object", - }, - "notes": Object { - "type": "keyword", }, + type: 'object', + }, + notes: { + type: 'keyword', }, - "type": "object", }, + type: 'object', }, }, - "user": Object { - "properties": Object { - "name": Object { - "type": "keyword", - }, - "risk": Object { - "properties": Object { - "calculated_level": Object { - "type": "keyword", - }, - "calculated_score": Object { - "type": "float", - }, - "calculated_score_norm": Object { - "type": "float", - }, - "category_1_count": Object { - "type": "long", - }, - "category_1_score": Object { - "type": "float", - }, - "id_field": Object { - "type": "keyword", - }, - "id_value": Object { - "type": "keyword", - }, - "inputs": Object { - "properties": Object { - "category": Object { - "type": "keyword", - }, - "description": Object { - "type": "keyword", - }, - "id": Object { - "type": "keyword", - }, - "index": Object { - "type": "keyword", - }, - "risk_score": Object { - "type": "float", - }, - "timestamp": Object { - "type": "date", - }, + }, + user: { + properties: { + name: { + type: 'keyword', + }, + risk: { + properties: { + calculated_level: { + type: 'keyword', + }, + calculated_score: { + type: 'float', + }, + calculated_score_norm: { + type: 'float', + }, + category_1_count: { + type: 'long', + }, + category_1_score: { + type: 'float', + }, + id_field: { + type: 'keyword', + }, + id_value: { + type: 'keyword', + }, + inputs: { + properties: { + category: { + type: 'keyword', + }, + description: { + type: 'keyword', + }, + id: { + type: 'keyword', + }, + index: { + type: 'keyword', + }, + risk_score: { + type: 'float', + }, + timestamp: { + type: 'date', }, - "type": "object", - }, - "notes": Object { - "type": "keyword", }, + type: 'object', + }, + notes: { + type: 'keyword', }, - "type": "object", }, + type: 'object', }, }, }, }, - "settings": Object {}, - } - `); + }, + }, + }); + }; - expect(createOrUpdateIndexTemplate).toHaveBeenCalledWith({ - logger, - esClient, - template: { - name: '.risk-score.risk-score-default-index-template', - body: { - data_stream: { hidden: true }, - index_patterns: ['risk-score.risk-score-default'], - composed_of: ['.risk-score-mappings'], - template: { - lifecycle: {}, - settings: { - 'index.mapping.total_fields.limit': totalFieldsLimit, - }, - mappings: { - dynamic: false, - _meta: { - kibana: { - version: '8.9.0', - }, - managed: true, - namespace: 'default', - }, + const assertTransform = (namespace: string) => { + expect(transforms.createTransform).toHaveBeenCalledWith({ + logger, + esClient, + transform: { + dest: { + index: `risk-score.risk-score-latest-${namespace}`, + }, + frequency: '1h', + latest: { + sort: '@timestamp', + unique_key: ['host.name', 'user.name'], + }, + source: { + index: [`risk-score.risk-score-${namespace}`], + }, + sync: { + time: { + delay: '0s', + field: '@timestamp', }, }, + transform_id: `risk_score_latest_transform_${namespace}`, + settings: { + unattended: true, + }, _meta: { - kibana: { - version: '8.9.0', - }, + version: 2, managed: true, - namespace: 'default', + managed_by: 'security-entity-analytics', }, }, - }, - }); + }); + }; - expect(createDataStream).toHaveBeenCalledWith({ - logger, - esClient, - totalFieldsLimit, - indexPatterns: { - template: `.risk-score.risk-score-default-index-template`, - alias: `risk-score.risk-score-default`, - }, - }); + // Default namespace + esClient.cluster.existsComponentTemplate.mockResolvedValue(false); + await riskScoreDataClient.init(); + assertComponentTemplate('default'); + assertIndexTemplate('default'); + assertDataStream('default'); + assertIndex('default'); + assertTransform('default'); - expect(createOrUpdateIndex).toHaveBeenCalledWith({ - logger, - esClient, - options: { - index: `risk-score.risk-score-latest-default`, - mappings: { - dynamic: false, - properties: { - '@timestamp': { - ignore_malformed: false, - type: 'date', - }, - host: { - properties: { - name: { - type: 'keyword', - }, - risk: { - properties: { - calculated_level: { - type: 'keyword', - }, - calculated_score: { - type: 'float', - }, - calculated_score_norm: { - type: 'float', - }, - category_1_count: { - type: 'long', - }, - category_1_score: { - type: 'float', - }, - id_field: { - type: 'keyword', - }, - id_value: { - type: 'keyword', - }, - inputs: { - properties: { - category: { - type: 'keyword', - }, - description: { - type: 'keyword', - }, - id: { - type: 'keyword', - }, - index: { - type: 'keyword', - }, - risk_score: { - type: 'float', - }, - timestamp: { - type: 'date', + // Space-1 namespace + esClient.cluster.existsComponentTemplate.mockResolvedValue(false); + await riskScoreDataClientWithNameSpace.init(); + assertComponentTemplate('space-1'); + assertIndexTemplate('space-1'); + assertDataStream('space-1'); + assertIndex('space-1'); + assertTransform('space-1'); + + expect((createOrUpdateComponentTemplate as jest.Mock).mock.lastCall[0].template.template) + .toMatchInlineSnapshot(` + Object { + "mappings": Object { + "dynamic": "strict", + "properties": Object { + "@timestamp": Object { + "ignore_malformed": false, + "type": "date", + }, + "host": Object { + "properties": Object { + "name": Object { + "type": "keyword", + }, + "risk": Object { + "properties": Object { + "calculated_level": Object { + "type": "keyword", + }, + "calculated_score": Object { + "type": "float", + }, + "calculated_score_norm": Object { + "type": "float", + }, + "category_1_count": Object { + "type": "long", + }, + "category_1_score": Object { + "type": "float", + }, + "id_field": Object { + "type": "keyword", + }, + "id_value": Object { + "type": "keyword", + }, + "inputs": Object { + "properties": Object { + "category": Object { + "type": "keyword", + }, + "description": Object { + "type": "keyword", + }, + "id": Object { + "type": "keyword", + }, + "index": Object { + "type": "keyword", + }, + "risk_score": Object { + "type": "float", + }, + "timestamp": Object { + "type": "date", + }, }, + "type": "object", + }, + "notes": Object { + "type": "keyword", }, - type: 'object', - }, - notes: { - type: 'keyword', }, + "type": "object", }, - type: 'object', }, }, - }, - user: { - properties: { - name: { - type: 'keyword', - }, - risk: { - properties: { - calculated_level: { - type: 'keyword', - }, - calculated_score: { - type: 'float', - }, - calculated_score_norm: { - type: 'float', - }, - category_1_count: { - type: 'long', - }, - category_1_score: { - type: 'float', - }, - id_field: { - type: 'keyword', - }, - id_value: { - type: 'keyword', - }, - inputs: { - properties: { - category: { - type: 'keyword', - }, - description: { - type: 'keyword', - }, - id: { - type: 'keyword', - }, - index: { - type: 'keyword', - }, - risk_score: { - type: 'float', - }, - timestamp: { - type: 'date', + "user": Object { + "properties": Object { + "name": Object { + "type": "keyword", + }, + "risk": Object { + "properties": Object { + "calculated_level": Object { + "type": "keyword", + }, + "calculated_score": Object { + "type": "float", + }, + "calculated_score_norm": Object { + "type": "float", + }, + "category_1_count": Object { + "type": "long", + }, + "category_1_score": Object { + "type": "float", + }, + "id_field": Object { + "type": "keyword", + }, + "id_value": Object { + "type": "keyword", + }, + "inputs": Object { + "properties": Object { + "category": Object { + "type": "keyword", + }, + "description": Object { + "type": "keyword", + }, + "id": Object { + "type": "keyword", + }, + "index": Object { + "type": "keyword", + }, + "risk_score": Object { + "type": "float", + }, + "timestamp": Object { + "type": "date", + }, }, + "type": "object", + }, + "notes": Object { + "type": "keyword", }, - type: 'object', - }, - notes: { - type: 'keyword', }, + "type": "object", }, - type: 'object', }, }, }, }, - }, - }, - }); - - expect(transforms.createTransform).toHaveBeenCalledWith({ - logger, - esClient, - transform: { - dest: { - index: 'risk-score.risk-score-latest-default', - }, - frequency: '1h', - latest: { - sort: '@timestamp', - unique_key: ['host.name', 'user.name'], - }, - source: { - index: ['risk-score.risk-score-default'], - }, - sync: { - time: { - delay: '0s', - field: '@timestamp', - }, - }, - transform_id: 'risk_score_latest_transform_default', - settings: { - unattended: true, - }, - _meta: { - version: 2, - managed: true, - managed_by: 'security-entity-analytics', - }, - }, - }); + "settings": Object {}, + } + `); }); }); @@ -479,7 +509,7 @@ describe('RiskScoreDataClient', () => { expect(esClient.transform.deleteTransform).toHaveBeenCalledTimes(1); expect(esClient.indices.deleteDataStream).toHaveBeenCalledTimes(1); expect(esClient.indices.deleteIndexTemplate).toHaveBeenCalledTimes(1); - expect(esClient.cluster.deleteComponentTemplate).toHaveBeenCalledTimes(1); + expect(esClient.cluster.deleteComponentTemplate).toHaveBeenCalledTimes(2); expect(errors).toEqual([]); }); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts index 0b3c38f3602fd..2ae05e4c86227 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts @@ -22,6 +22,7 @@ import { getIndexPatternDataStream, getTransformOptions, mappingComponentName, + nameSpaceAwareMappingsComponentName, riskScoreFieldMap, totalFieldsLimit, } from './configurations'; @@ -114,12 +115,42 @@ export class RiskScoreDataClient { namespace, }; + // Check if there are any existing component templates with the namespace in the name + + const oldComponentTemplateExists = await esClient.cluster.existsComponentTemplate({ + name: mappingComponentName, + }); + // If present then copy the contents to a new component template with the namespace in the name + if (oldComponentTemplateExists) { + const oldComponentTemplateResponse = await esClient.cluster.getComponentTemplate( + { + name: mappingComponentName, + }, + { ignore: [404] } + ); + const oldComponentTemplate = oldComponentTemplateResponse?.component_templates[0]; + const newComponentTemplateName = nameSpaceAwareMappingsComponentName(namespace); + await esClient.cluster.putComponentTemplate({ + name: newComponentTemplateName, + body: oldComponentTemplate.component_template, + }); + } + + // Delete the component template without the namespace in the name + await esClient.cluster.deleteComponentTemplate( + { + name: mappingComponentName, + }, + { ignore: [404] } + ); + + // Update the new component template with the required data await Promise.all([ createOrUpdateComponentTemplate({ logger: this.options.logger, esClient, template: { - name: mappingComponentName, + name: nameSpaceAwareMappingsComponentName(namespace), _meta: { managed: true, }, @@ -132,6 +163,7 @@ export class RiskScoreDataClient { }), ]); + // Reference the new component template in the index template await createOrUpdateIndexTemplate({ logger: this.options.logger, esClient, @@ -140,7 +172,7 @@ export class RiskScoreDataClient { body: { data_stream: { hidden: true }, index_patterns: [indexPatterns.alias], - composed_of: [mappingComponentName], + composed_of: [nameSpaceAwareMappingsComponentName(namespace)], template: { lifecycle: {}, settings: { @@ -235,6 +267,15 @@ export class RiskScoreDataClient { ) .catch(addError); + await esClient.cluster + .deleteComponentTemplate( + { + name: nameSpaceAwareMappingsComponentName(namespace), + }, + { ignore: [404] } + ) + .catch(addError); + await esClient.cluster .deleteComponentTemplate( { diff --git a/x-pack/plugins/security_solution/server/ui_settings.ts b/x-pack/plugins/security_solution/server/ui_settings.ts index 842b8bbeceff8..6aeb86750be7a 100644 --- a/x-pack/plugins/security_solution/server/ui_settings.ts +++ b/x-pack/plugins/security_solution/server/ui_settings.ts @@ -343,6 +343,7 @@ export const initUiSettings = ( max: 1000, defaultValue: DEFAULT_MAX_UNASSOCIATED_NOTES, }), + category: [APP_ID], requiresPageReload: false, }, [EXCLUDED_DATA_TIERS_FOR_RULE_EXECUTION]: { diff --git a/x-pack/plugins/security_solution_ess/public/upselling/register_upsellings.tsx b/x-pack/plugins/security_solution_ess/public/upselling/register_upsellings.tsx index b7fbdab3b5982..69f3c5dd4cc28 100644 --- a/x-pack/plugins/security_solution_ess/public/upselling/register_upsellings.tsx +++ b/x-pack/plugins/security_solution_ess/public/upselling/register_upsellings.tsx @@ -12,6 +12,7 @@ import { ALERT_SUPPRESSION_RULE_FORM, UPGRADE_ALERT_ASSIGNMENTS, UPGRADE_INVESTIGATION_GUIDE, + UPGRADE_NOTES_MANAGEMENT_USER_FILTER, } from '@kbn/security-solution-upselling/messages'; import type { MessageUpsellings, @@ -132,4 +133,9 @@ export const upsellingMessages: UpsellingMessages = [ minimumLicenseRequired: 'platinum', message: ALERT_SUPPRESSION_RULE_DETAILS, }, + { + id: 'note_management_user_filter', + minimumLicenseRequired: 'platinum', + message: UPGRADE_NOTES_MANAGEMENT_USER_FILTER('Platinum'), + }, ]; diff --git a/x-pack/plugins/serverless/public/plugin.tsx b/x-pack/plugins/serverless/public/plugin.tsx index ee2b32e4d97a5..82578123452e7 100644 --- a/x-pack/plugins/serverless/public/plugin.tsx +++ b/x-pack/plugins/serverless/public/plugin.tsx @@ -125,7 +125,7 @@ export class ServerlessPlugin getNavigationCards: (roleManagementEnabled, extendCardNavDefinitions) => { if (!roleManagementEnabled) return extendCardNavDefinitions; - const manageOrgMembersNavCard = generateManageOrgMembersNavCard(cloud.organizationUrl); + const manageOrgMembersNavCard = generateManageOrgMembersNavCard(cloud.usersAndRolesUrl); if (extendCardNavDefinitions) { extendCardNavDefinitions[manageOrgMembersNavCardName] = manageOrgMembersNavCard; return extendCardNavDefinitions; diff --git a/x-pack/plugins/serverless_search/server/routes/indices_routes.ts b/x-pack/plugins/serverless_search/server/routes/indices_routes.ts index 6b7ba424fde22..ed68cc77fa41a 100644 --- a/x-pack/plugins/serverless_search/server/routes/indices_routes.ts +++ b/x-pack/plugins/serverless_search/server/routes/indices_routes.ts @@ -107,6 +107,7 @@ export const registerIndicesRoutes = ({ logger, router }: RouteDependencies) => searchQuery: schema.string({ defaultValue: '', }), + trackTotalHits: schema.boolean({ defaultValue: false }), }), params: schema.object({ index_name: schema.string(), @@ -126,8 +127,16 @@ export const registerIndicesRoutes = ({ logger, router }: RouteDependencies) => const searchQuery = request.body.searchQuery; const { page = 0, size = DEFAULT_DOCS_PER_PAGE } = request.query; const from = page * size; + const trackTotalHits = request.body.trackTotalHits; - const searchResults = await fetchSearchResults(client, indexName, searchQuery, from, size); + const searchResults = await fetchSearchResults( + client, + indexName, + searchQuery, + from, + size, + trackTotalHits + ); return response.ok({ body: { diff --git a/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.test.tsx b/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.test.tsx index abb184bcb4382..169f12c3487c4 100644 --- a/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.test.tsx +++ b/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.test.tsx @@ -241,7 +241,7 @@ describe('SpacesGridPage', () => { wrapper.update(); const activeRow = wrapper.find('[data-test-subj="spacesListTableRow-custom-2"]'); - const switchAction = activeRow.find('EuiButtonIcon[data-test-subj="Custom 2-switchSpace"]'); + const switchAction = activeRow.find('EuiButtonIcon[data-test-subj="custom-2-switchSpace"]'); expect(switchAction.prop('isDisabled')).toBe(true); }); @@ -273,7 +273,7 @@ describe('SpacesGridPage', () => { wrapper.update(); const nonActiveRow = wrapper.find('[data-test-subj="spacesListTableRow-default"]'); - const switchAction = nonActiveRow.find('EuiButtonIcon[data-test-subj="Default-switchSpace"]'); + const switchAction = nonActiveRow.find('EuiButtonIcon[data-test-subj="default-switchSpace"]'); expect(switchAction.prop('isDisabled')).toBe(false); }); diff --git a/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.tsx b/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.tsx index 3049fb00d8977..586992c1b6b48 100644 --- a/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.tsx +++ b/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.tsx @@ -430,7 +430,7 @@ export class SpacesGridPage extends Component { reactRouterNavigate(this.props.history, this.getEditSpacePath(rowRecord)).href, onClick: (rowRecord) => reactRouterNavigate(this.props.history, this.getEditSpacePath(rowRecord)).onClick, - 'data-test-subj': (rowRecord) => `${rowRecord.name}-editSpace`, + 'data-test-subj': (rowRecord) => `${rowRecord.id}-editSpace`, }, { isPrimary: true, @@ -438,7 +438,7 @@ export class SpacesGridPage extends Component { defaultMessage: 'Switch', }), description: (rowRecord) => - activeSpace?.name !== rowRecord.name + activeSpace?.id !== rowRecord.id ? i18n.translate( 'xpack.spaces.management.spacesGridPage.switchSpaceActionDescription', { @@ -462,8 +462,8 @@ export class SpacesGridPage extends Component { rowRecord.id, `${ENTER_SPACE_PATH}?next=/app/management/kibana/spaces/` ), - enabled: (rowRecord) => activeSpace?.name !== rowRecord.name, - 'data-test-subj': (rowRecord) => `${rowRecord.name}-switchSpace`, + enabled: (rowRecord) => activeSpace?.id !== rowRecord.id, + 'data-test-subj': (rowRecord) => `${rowRecord.id}-switchSpace`, }, { name: i18n.translate('xpack.spaces.management.spacesGridPage.deleteActionName', { @@ -487,7 +487,7 @@ export class SpacesGridPage extends Component { color: 'danger', onClick: (rowRecord: Space) => this.onDeleteSpaceClick(rowRecord), enabled: (rowRecord: Space) => !isReservedSpace(rowRecord), - 'data-test-subj': (rowRecord) => `${rowRecord.name}-deleteSpace`, + 'data-test-subj': (rowRecord) => `${rowRecord.id}-deleteSpace`, }, ], }); diff --git a/x-pack/plugins/task_manager/server/task_claimers/strategy_mget.ts b/x-pack/plugins/task_manager/server/task_claimers/strategy_mget.ts index aa69742998c74..407cf6b90dd6c 100644 --- a/x-pack/plugins/task_manager/server/task_claimers/strategy_mget.ts +++ b/x-pack/plugins/task_manager/server/task_claimers/strategy_mget.ts @@ -357,7 +357,10 @@ async function searchAvailableTasks({ // Task must be enabled EnabledTask, // a task type that's not excluded (may be removed or not) - OneOfTaskTypes('task.taskType', claimPartitions.unlimitedTypes), + OneOfTaskTypes( + 'task.taskType', + claimPartitions.unlimitedTypes.concat(Array.from(removedTypes)) + ), // Either a task with idle status and runAt <= now or // status running or claiming with a retryAt <= now. shouldBeOneOf(IdleTaskWithExpiredRunAt, RunningOrClaimingTaskWithExpiredRetryAt), diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 61d08b0c81a3b..fc240ba84ad29 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -8261,8 +8261,6 @@ "visTypeMarkdown.function.help": "Visualisation Markdown", "visTypeMarkdown.function.markdown.help": "Markdown à rendre", "visTypeMarkdown.function.openLinksInNewTab.help": "Ouvre les liens dans un nouvel onglet", - "visTypeMarkdown.markdownDescription": "Ajoutez du texte et des images à votre tableau de bord.", - "visTypeMarkdown.markdownTitleInWizard": "Texte", "visTypeMarkdown.params.fontSizeLabel": "Taille de police de base en points", "visTypeMarkdown.params.helpLinkLabel": "Aide", "visTypeMarkdown.params.openLinksLabel": "Ouvrir les liens dans un nouvel onglet", @@ -8591,7 +8589,6 @@ "visTypeTimeseries.indexPatternSelect.switchModePopover.title": "Mode de vue de données", "visTypeTimeseries.indexPatternSelect.switchModePopover.useKibanaIndices": "Utiliser des vues de données Kibana", "visTypeTimeseries.indexPatternSelect.updateIndex": "Mettre à jour la visualisation avec la vue de données saisie", - "visTypeTimeseries.kbnVisTypes.metricsDescription": "Réalisez des analyses avancées de vos données temporelles.", "visTypeTimeseries.kbnVisTypes.metricsTitle": "TSVB", "visTypeTimeseries.lastValueModeIndicator.lastBucketDate": "Compartiment : {lastBucketDate}", "visTypeTimeseries.lastValueModeIndicator.lastValue": "Dernière valeur", @@ -8969,7 +8966,6 @@ "visTypeVega.mapView.resettingPropertyToMaxValueWarningMessage": "Réinitialisation de {name} sur {max}", "visTypeVega.mapView.resettingPropertyToMinValueWarningMessage": "Réinitialisation de {name} sur {min}", "visTypeVega.type.vegaDescription": "Utilisez Vega pour créer de nouveaux types de visualisations.", - "visTypeVega.type.vegaNote": "Requiert une connaissance de la syntaxe Vega.", "visTypeVega.type.vegaTitleInWizard": "Visualisation personnalisée", "visTypeVega.urlParser.dataUrlRequiresUrlParameterInFormErrorMessage": "{dataUrlParam} requiert un paramètre {urlParam} sous la forme \"{formLink}\"", "visTypeVega.urlParser.urlShouldHaveQuerySubObjectWarningMessage": "L'utilisation d'un {urlObject} requiert un sous-objet {subObjectName}", @@ -9154,7 +9150,6 @@ "visualizations.createVisualization.noIndexPatternOrSavedSearchIdErrorMessage": "Vous devez fournir un indexPattern ou un savedSearchId", "visualizations.createVisualization.noVisTypeErrorMessage": "Vous devez fournir un type de visualisation valide", "visualizations.dataView.label": "Vue de données", - "visualizations.deprecatedTag": "Déclassé", "visualizations.displayName": "visualisation", "visualizations.editor.createBreadcrumb": "Créer", "visualizations.editor.defaultEditBreadcrumbText": "Modifier la visualisation", @@ -9206,24 +9201,16 @@ "visualizations.newChart.libraryMode.new": "nouveau", "visualizations.newChart.libraryMode.old": "âge", "visualizations.newGaugeChart.notificationMessage": "La nouvelle bibliothèque de graphiques de jauge ne prend pas encore en charge l'agrégation de graphiques fractionnés. {conditionalMessage}", - "visualizations.newVisWizard.aggBasedGroupDescription": "Utilisez notre bibliothèque Visualize classique pour créer des graphiques basés sur des agrégations.", - "visualizations.newVisWizard.aggBasedGroupTitle": "Basé sur une agrégation", "visualizations.newVisWizard.chooseSourceTitle": "Choisir une source", - "visualizations.newVisWizard.experimentalTitle": "Version d'évaluation technique", - "visualizations.newVisWizard.experimentalTooltip": "Cette fonctionnalité est en version d'évaluation technique et pourra être modifiée ou retirée complètement dans une future version. Elastic s'efforcera de corriger tout problème, mais les fonctionnalités des versions d'évaluation technique ne sont pas soumises aux SLA de support des fonctionnalités officielles en disponibilité générale.", - "visualizations.newVisWizard.exploreOptionLinkText": "Explorer les options", "visualizations.newVisWizard.filterVisTypeAriaLabel": "Filtrer un type de visualisation", "visualizations.newVisWizard.goBackLink": "Sélectionner une visualisation différente", "visualizations.newVisWizard.helpTextAriaLabel": "Commencez à créer votre visualisation en sélectionnant un type pour cette visualisation. Appuyez sur Échap pour fermer ce mode. Appuyez sur Tab pour aller plus loin.", "visualizations.newVisWizard.learnMoreText": "Envie d'en savoir plus ?", "visualizations.newVisWizard.newVisTypeTitle": "Nouveau {visTypeName}", - "visualizations.newVisWizard.readDocumentationLink": "Lire la documentation", "visualizations.newVisWizard.resultsFound": "{resultCount, plural, one {type trouvé} other {types trouvés}}", "visualizations.newVisWizard.searchSelection.notFoundLabel": "Aucun recherche enregistrée ni aucun index correspondants n'ont été trouvés.", "visualizations.newVisWizard.searchSelection.savedObjectType.dataView": "Vue de données", "visualizations.newVisWizard.searchSelection.savedObjectType.search": "Recherche enregistrée", - "visualizations.newVisWizard.title": "Nouvelle visualisation", - "visualizations.newVisWizard.toolsGroupTitle": "Outils", "visualizations.noDataView.label": "vue de données", "visualizations.noMatchRoute.bannerText": "L'application Visualize ne reconnaît pas cet itinéraire : {route}.", "visualizations.noMatchRoute.bannerTitleText": "Page introuvable", @@ -25772,8 +25759,6 @@ "xpack.lens.uniqueLabel": "{label} [{num}]", "xpack.lens.unknownDatasourceType.shortMessage": "Type de source de données inconnu", "xpack.lens.unknownVisType.shortMessage": "Type de visualisation inconnu", - "xpack.lens.visTypeAlias.description": "Créez des visualisations avec notre éditeur de glisser-déposer. Basculez entre les différents types de visualisation à tout moment.", - "xpack.lens.visTypeAlias.note": "Recommandé pour la plupart des utilisateurs.", "xpack.lens.visTypeAlias.title": "Lens", "xpack.lens.visTypeAlias.type": "Lens", "xpack.lens.visualizeAggBasedLegend": "Visualiser le graphique basé sur une agrégation", @@ -47491,4 +47476,4 @@ "xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "Ce champ est requis.", "xpack.watcher.watcherDescription": "Détectez les modifications survenant dans vos données en créant, gérant et monitorant des alertes." } -} \ No newline at end of file +} diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 61ad6c58ea44c..4d39022965da0 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -8015,8 +8015,6 @@ "visTypeMarkdown.function.help": "マークダウンビジュアライゼーション", "visTypeMarkdown.function.markdown.help": "レンダリングするマークダウン", "visTypeMarkdown.function.openLinksInNewTab.help": "新規タブでリンクを開きます", - "visTypeMarkdown.markdownDescription": "テキストと画像をダッシュボードに追加します。", - "visTypeMarkdown.markdownTitleInWizard": "Text", "visTypeMarkdown.params.fontSizeLabel": "ポイント単位のベースフォントサイズです。", "visTypeMarkdown.params.helpLinkLabel": "ヘルプ", "visTypeMarkdown.params.openLinksLabel": "新規タブでリンクを開く", @@ -8345,7 +8343,6 @@ "visTypeTimeseries.indexPatternSelect.switchModePopover.title": "データビューモード", "visTypeTimeseries.indexPatternSelect.switchModePopover.useKibanaIndices": "Kibanaデータビューを使用", "visTypeTimeseries.indexPatternSelect.updateIndex": "入力したデータビューで可視化を更新", - "visTypeTimeseries.kbnVisTypes.metricsDescription": "時系列データの高度な分析を実行します。", "visTypeTimeseries.kbnVisTypes.metricsTitle": "TSVB", "visTypeTimeseries.lastValueModeIndicator.lastBucketDate": "バケット:{lastBucketDate}", "visTypeTimeseries.lastValueModeIndicator.lastValue": "最終値", @@ -8722,7 +8719,6 @@ "visTypeVega.mapView.resettingPropertyToMaxValueWarningMessage": "{name} を {max} にリセットしています", "visTypeVega.mapView.resettingPropertyToMinValueWarningMessage": "{name} を {min} にリセットしています", "visTypeVega.type.vegaDescription": "Vega を使用して、新しいタイプのビジュアライゼーションを作成します。", - "visTypeVega.type.vegaNote": "Vega 構文の知識が必要です。", "visTypeVega.type.vegaTitleInWizard": "カスタムビジュアライゼーション", "visTypeVega.urlParser.dataUrlRequiresUrlParameterInFormErrorMessage": "{dataUrlParam} には「{formLink}」の形で {urlParam} パラメーターが必要です", "visTypeVega.urlParser.urlShouldHaveQuerySubObjectWarningMessage": "{urlObject} を使用するには {subObjectName} サブオブジェクトが必要です", @@ -8907,7 +8903,6 @@ "visualizations.createVisualization.noIndexPatternOrSavedSearchIdErrorMessage": "indexPatternまたはsavedSearchIdが必要です", "visualizations.createVisualization.noVisTypeErrorMessage": "有効なビジュアライゼーションタイプを指定してください", "visualizations.dataView.label": "データビュー", - "visualizations.deprecatedTag": "非推奨", "visualizations.displayName": "ビジュアライゼーション", "visualizations.editor.createBreadcrumb": "作成", "visualizations.editor.defaultEditBreadcrumbText": "ビジュアライゼーションを編集", @@ -8959,24 +8954,16 @@ "visualizations.newChart.libraryMode.new": "新規", "visualizations.newChart.libraryMode.old": "古", "visualizations.newGaugeChart.notificationMessage": "新しいゲージグラフライブラリはまだ分割グラフアグリゲーションをサポートしていません。{conditionalMessage}", - "visualizations.newVisWizard.aggBasedGroupDescription": "クラシック Visualize ライブラリを使用して、アグリゲーションに基づいてグラフを作成します。", - "visualizations.newVisWizard.aggBasedGroupTitle": "アグリゲーションに基づく", "visualizations.newVisWizard.chooseSourceTitle": "ソースの選択", - "visualizations.newVisWizard.experimentalTitle": "テクニカルプレビュー", - "visualizations.newVisWizard.experimentalTooltip": "この機能はテクニカルプレビュー中であり、将来のリリースでは変更されたり完全に削除されたりする場合があります。Elasticはすべての問題の修正に努めますが、テクニカルプレビュー中の機能には正式なGA機能のサポートSLAが適用されません。", - "visualizations.newVisWizard.exploreOptionLinkText": "探索オプション", "visualizations.newVisWizard.filterVisTypeAriaLabel": "ビジュアライゼーションのタイプでフィルタリング", "visualizations.newVisWizard.goBackLink": "別のビジュアライゼーションを選択", "visualizations.newVisWizard.helpTextAriaLabel": "タイプを選択してビジュアライゼーションの作成を始めましょう。ESC を押してこのモーダルを閉じます。Tab キーを押して次に進みます。", "visualizations.newVisWizard.learnMoreText": "詳細について", "visualizations.newVisWizard.newVisTypeTitle": "新規 {visTypeName}", - "visualizations.newVisWizard.readDocumentationLink": "ドキュメンテーションを表示", "visualizations.newVisWizard.resultsFound": "{resultCount, plural, other {個のタイプ}} が見つかりました", "visualizations.newVisWizard.searchSelection.notFoundLabel": "一致インデックスまたは保存した検索が見つかりません。", "visualizations.newVisWizard.searchSelection.savedObjectType.dataView": "データビュー", "visualizations.newVisWizard.searchSelection.savedObjectType.search": "保存検索", - "visualizations.newVisWizard.title": "新規ビジュアライゼーション", - "visualizations.newVisWizard.toolsGroupTitle": "ツール", "visualizations.noDataView.label": "データビュー", "visualizations.noMatchRoute.bannerText": "Visualizeアプリケーションはこのルートを認識できません。{route}", "visualizations.noMatchRoute.bannerTitleText": "ページが見つかりません", @@ -25520,8 +25507,6 @@ "xpack.lens.unknownDatasourceType.shortMessage": "不明なデータソースタイプ", "xpack.lens.unknownVisType.longMessage": "ビジュアライゼーションタイプ{visType}を解決できませんでした。", "xpack.lens.unknownVisType.shortMessage": "不明なビジュアライゼーションタイプ", - "xpack.lens.visTypeAlias.description": "ドラッグアンドドロップエディターでビジュアライゼーションを作成します。いつでもビジュアライゼーションタイプを切り替えることができます。", - "xpack.lens.visTypeAlias.note": "ほとんどのユーザーに推奨されます。", "xpack.lens.visTypeAlias.title": "Lens", "xpack.lens.visTypeAlias.type": "Lens", "xpack.lens.visualizeAggBasedLegend": "集約に基づくグラフを可視化", @@ -47229,4 +47214,4 @@ "xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "フィールドを選択してください。", "xpack.watcher.watcherDescription": "アラートの作成、管理、監視によりデータへの変更を検知します。" } -} \ No newline at end of file +} diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 22061c2c36715..27ee1dd00a563 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -8031,8 +8031,6 @@ "visTypeMarkdown.function.help": "Markdown 可视化", "visTypeMarkdown.function.markdown.help": "要渲染的 Markdown", "visTypeMarkdown.function.openLinksInNewTab.help": "在新标签页中打开链接", - "visTypeMarkdown.markdownDescription": "将文本和图像添加到仪表板。", - "visTypeMarkdown.markdownTitleInWizard": "文本", "visTypeMarkdown.params.fontSizeLabel": "基础字体大小(磅)", "visTypeMarkdown.params.helpLinkLabel": "帮助", "visTypeMarkdown.params.openLinksLabel": "在新标签页中打开链接", @@ -8361,7 +8359,6 @@ "visTypeTimeseries.indexPatternSelect.switchModePopover.title": "数据视图模式", "visTypeTimeseries.indexPatternSelect.switchModePopover.useKibanaIndices": "使用 Kibana 数据视图", "visTypeTimeseries.indexPatternSelect.updateIndex": "使用输入的数据视图更新可视化", - "visTypeTimeseries.kbnVisTypes.metricsDescription": "对时间序列数据执行高级分析。", "visTypeTimeseries.kbnVisTypes.metricsTitle": "TSVB", "visTypeTimeseries.lastValueModeIndicator.lastBucketDate": "存储桶:{lastBucketDate}", "visTypeTimeseries.lastValueModeIndicator.lastValue": "最后值", @@ -8739,7 +8736,6 @@ "visTypeVega.mapView.resettingPropertyToMaxValueWarningMessage": "将 {name} 重置为 {max}", "visTypeVega.mapView.resettingPropertyToMinValueWarningMessage": "将 {name} 重置为 {min}", "visTypeVega.type.vegaDescription": "使用 Vega 创建新的可视化类型。", - "visTypeVega.type.vegaNote": "需要有 Vega 语法知识。", "visTypeVega.type.vegaTitleInWizard": "定制可视化", "visTypeVega.urlParser.dataUrlRequiresUrlParameterInFormErrorMessage": "{dataUrlParam} 需要“{formLink}”形式的 {urlParam} 参数", "visTypeVega.urlParser.urlShouldHaveQuerySubObjectWarningMessage": "使用 {urlObject} 应具有 {subObjectName} 子对象", @@ -8924,7 +8920,6 @@ "visualizations.createVisualization.noIndexPatternOrSavedSearchIdErrorMessage": "必须提供 indexPattern 或 savedSearchId", "visualizations.createVisualization.noVisTypeErrorMessage": "必须提供有效的可视化类型", "visualizations.dataView.label": "数据视图", - "visualizations.deprecatedTag": "(已过时)", "visualizations.displayName": "可视化", "visualizations.editor.createBreadcrumb": "创建", "visualizations.editor.defaultEditBreadcrumbText": "编辑可视化", @@ -8976,24 +8971,16 @@ "visualizations.newChart.libraryMode.new": "新", "visualizations.newChart.libraryMode.old": "以前", "visualizations.newGaugeChart.notificationMessage": "新的仪表盘图表库尚不支持拆分图表聚合。{conditionalMessage}", - "visualizations.newVisWizard.aggBasedGroupDescription": "使用我们的经典可视化库,基于聚合创建图表。", - "visualizations.newVisWizard.aggBasedGroupTitle": "基于聚合", "visualizations.newVisWizard.chooseSourceTitle": "选择源", - "visualizations.newVisWizard.experimentalTitle": "技术预览", - "visualizations.newVisWizard.experimentalTooltip": "此功能处于技术预览状态,在未来版本中可能会更改或完全移除。Elastic 将努力修复任何问题,但处于技术预览状态的功能不受正式 GA 功能支持 SLA 的约束。", - "visualizations.newVisWizard.exploreOptionLinkText": "浏览选项", "visualizations.newVisWizard.filterVisTypeAriaLabel": "筛留可视化类型", "visualizations.newVisWizard.goBackLink": "选择不同的可视化", "visualizations.newVisWizard.helpTextAriaLabel": "通过为该可视化选择类型,开始创建您的可视化。按 Esc 键关闭此模式。按 Tab 键继续。", "visualizations.newVisWizard.learnMoreText": "希望了解详情?", "visualizations.newVisWizard.newVisTypeTitle": "新建{visTypeName}", - "visualizations.newVisWizard.readDocumentationLink": "阅读文档", "visualizations.newVisWizard.resultsFound": "{resultCount, plural, other {类型}}已找到", "visualizations.newVisWizard.searchSelection.notFoundLabel": "未找到匹配的索引或已保存搜索。", "visualizations.newVisWizard.searchSelection.savedObjectType.dataView": "数据视图", "visualizations.newVisWizard.searchSelection.savedObjectType.search": "已保存搜索", - "visualizations.newVisWizard.title": "新建可视化", - "visualizations.newVisWizard.toolsGroupTitle": "工具", "visualizations.noDataView.label": "数据视图", "visualizations.noMatchRoute.bannerText": "Visualize 应用程序无法识别此路由:{route}。", "visualizations.noMatchRoute.bannerTitleText": "未找到页面", @@ -25555,8 +25542,6 @@ "xpack.lens.unknownDatasourceType.shortMessage": "数据源类型未知", "xpack.lens.unknownVisType.longMessage": "无法解析可视化类型 {visType}。", "xpack.lens.unknownVisType.shortMessage": "可视化类型未知", - "xpack.lens.visTypeAlias.description": "使用拖放编辑器创建可视化。随时在可视化类型之间切换。", - "xpack.lens.visTypeAlias.note": "适合绝大多数用户。", "xpack.lens.visTypeAlias.title": "Lens", "xpack.lens.visTypeAlias.type": "Lens", "xpack.lens.visualizeAggBasedLegend": "可视化基于聚合的图表", @@ -47282,4 +47267,4 @@ "xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "此字段必填。", "xpack.watcher.watcherDescription": "通过创建、管理和监测警报来检测数据中的更改。" } -} \ No newline at end of file +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/alerts_count/alerts_count.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/alerts_count/alerts_count.tsx index 50eb14ccfa3df..45ca382686752 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/alerts_count/alerts_count.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/alerts_count/alerts_count.tsx @@ -33,6 +33,7 @@ export const AlertsCount = ({ count }: { count: number }) => { border-right: ${euiTheme.border.thin}; margin-right: ${euiTheme.size.s}; padding-right: ${euiTheme.size.m}; + align-self: center; `} > {alertCountText} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/index.tsx index f2631b9fc0f26..73005464a80c7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/index.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/last_updated_at/index.tsx @@ -9,6 +9,7 @@ import { EuiText, EuiToolTip } from '@elastic/eui'; import { FormattedRelative } from '@kbn/i18n-react'; import React, { useEffect, useMemo, useState } from 'react'; +import { css } from '@emotion/react'; import * as i18n from './translations'; export interface LastUpdatedAtProps { @@ -36,6 +37,12 @@ Updated.displayName = 'Updated'; const prefix = ` ${i18n.UPDATED} `; +const anchorStyles = { + css: css` + align-self: center; + `, +}; + export const LastUpdatedAt = React.memo( ({ compact = false, updatedAt, showUpdating = false }) => { const [date, setDate] = useState(Date.now()); @@ -64,7 +71,10 @@ export const LastUpdatedAt = React.memo( }, [compact, date, showUpdating, updatedAt]); return ( - }> + } + anchorProps={anchorStyles} + > {updateText} diff --git a/x-pack/plugins/upgrade_assistant/README.md b/x-pack/plugins/upgrade_assistant/README.md index 30b403f6d6230..2acac8e3e734d 100644 --- a/x-pack/plugins/upgrade_assistant/README.md +++ b/x-pack/plugins/upgrade_assistant/README.md @@ -17,7 +17,7 @@ When we want to enable ML model snapshot deprecation warnings again we need to c * Migrating system indices (`featureSet.migrateSystemIndices`): Migrating system indices should only be enabled for major version upgrades. This config hides the second step from the UA UI for migrating system indices. * Reindex corrective actions (`featureSet.reindexCorrectiveActions`): Deprecations with reindexing corrective actions are only enabled for major version upgrades. Currently, the reindex actions include some logic that is specific to the [8.0 upgrade](https://github.com/elastic/kibana/blob/main/x-pack/plugins/upgrade_assistant/server/lib/reindexing/index_settings.ts). End users could get into a bad situation if this is enabled before this logic is fixed. -### Deprecations +## Deprecations There are three sources of deprecation information: @@ -275,6 +275,47 @@ PUT .reporting-*/_settings } ``` +#### Kibana API deprecations: +Run kibana locally with the test example plugin that has deprecated routes +``` +yarn start --plugin-path=examples/routing_example --plugin-path=examples/developer_examples +``` + +The following comprehensive deprecated routes examples are registered inside the folder: `examples/routing_example/server/routes/deprecated_routes` + +Run them in the console to trigger the deprecation condition so they show up in the UA: + +``` +# Versioned routes: Version 1 is deprecated +GET kbn:/api/routing_example/d/versioned?apiVersion=1 +GET kbn:/api/routing_example/d/versioned?apiVersion=2 + +# Non-versioned routes +GET kbn:/api/routing_example/d/removed_route +POST kbn:/api/routing_example/d/migrated_route +{} +``` + +1. You can also mark as deprecated in the UA to remove the deprecation from the list. +2. Check the telemetry response to see the reported data about the deprecated route. +3. Calling version 2 of the API does not do anything since it is not deprecated unlike version `1` (`GET kbn:/api/routing_example/d/versioned?apiVersion=2`) +4. Internally you can see the deprecations counters from the dev console by running the following: +``` +GET .kibana_usage_counters/_search +{ + "query": { + "bool": { + "should": [ + {"match": { "usage-counter.counterType": "deprecated_api_call:total"}}, + {"match": { "usage-counter.counterType": "deprecated_api_call:resolved"}}, + {"match": { "usage-counter.counterType": "deprecated_api_call:marked_as_resolved"}} + ] + } + } +} + +``` + For a complete list of Kibana deprecations, refer to the [8.0 Kibana deprecations meta issue](https://github.com/elastic/kibana/issues/109166). ### Errors diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/deprecation_details_flyout.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/deprecation_details_flyout.tsx index a6ea9a26c9bb8..beb4c7c0c678c 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/deprecation_details_flyout.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/deprecation_details_flyout.tsx @@ -50,6 +50,12 @@ const i18nTexts = { defaultMessage: 'Quick resolve', } ), + markAsResolvedButtonLabel: i18n.translate( + 'xpack.upgradeAssistant.kibanaDeprecations.flyout.quickResolveButtonLabel', + { + defaultMessage: 'Mark as Resolved', + } + ), retryQuickResolveButtonLabel: i18n.translate( 'xpack.upgradeAssistant.kibanaDeprecations.flyout.retryQuickResolveButtonLabel', { @@ -97,7 +103,15 @@ const i18nTexts = { ), }; -const getQuickResolveButtonLabel = (deprecationResolutionState?: DeprecationResolutionState) => { +interface AvailableCorrectiveActions { + api: boolean; + manual: boolean; + markAsResolved: boolean; +} +const getQuickResolveButtonLabel = ( + deprecationResolutionState: DeprecationResolutionState | undefined, + avilableCorrectiveActions: AvailableCorrectiveActions +) => { if (deprecationResolutionState?.resolveDeprecationStatus === 'in_progress') { return i18nTexts.quickResolveInProgressButtonLabel; } @@ -110,7 +124,13 @@ const getQuickResolveButtonLabel = (deprecationResolutionState?: DeprecationReso return i18nTexts.retryQuickResolveButtonLabel; } - return i18nTexts.quickResolveButtonLabel; + if (avilableCorrectiveActions.api) { + return i18nTexts.quickResolveButtonLabel; + } + + if (avilableCorrectiveActions.markAsResolved) { + return i18nTexts.markAsResolvedButtonLabel; + } }; export const DeprecationDetailsFlyout = ({ @@ -120,9 +140,19 @@ export const DeprecationDetailsFlyout = ({ deprecationResolutionState, }: DeprecationDetailsFlyoutProps) => { const { documentationUrl, message, correctiveActions, title } = deprecation; + const messages = Array.isArray(message) ? message : [message]; + const isCurrent = deprecationResolutionState?.id === deprecation.id; + const avilableCorrectiveActions: AvailableCorrectiveActions = { + api: !!correctiveActions.api, + manual: correctiveActions.manualSteps && correctiveActions.manualSteps.length > 0, + markAsResolved: !!correctiveActions.mark_as_resolved_api, + }; const isResolved = isCurrent && deprecationResolutionState?.resolveDeprecationStatus === 'ok'; + const hasResolveButton = + avilableCorrectiveActions.api || avilableCorrectiveActions.markAsResolved; + const onResolveDeprecation = useCallback(() => { uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_KIBANA_QUICK_RESOLVE_CLICK); resolveDeprecation(deprecation); @@ -155,7 +185,11 @@ export const DeprecationDetailsFlyout = ({ )} -

{message}

+ {messages.map((m, i) => ( +

+ {m} +

+ ))} {documentationUrl && (

@@ -221,7 +255,7 @@ export const DeprecationDetailsFlyout = ({ {/* Only show the "Quick resolve" button if deprecation supports it and deprecation is not yet resolved */} - {correctiveActions.api && !isResolved && ( + {hasResolveButton && !isResolved && ( - {getQuickResolveButtonLabel(deprecationResolutionState)} + {getQuickResolveButtonLabel(deprecationResolutionState, avilableCorrectiveActions)} )} diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations.tsx index d76f1afa9e612..0d433a59ee2d9 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations.tsx @@ -126,9 +126,9 @@ export const KibanaDeprecationsList = ({ const [flyoutContent, setFlyoutContent] = useState( undefined ); - const [deprecationResolutionState, setDeprecationResolutionState] = useState< - DeprecationResolutionState | undefined - >(undefined); + const [deprecationResolutionStates, setDeprecationResolutionStates] = useState< + Record + >({}); const { services: { @@ -194,17 +194,25 @@ export const KibanaDeprecationsList = ({ const resolveDeprecation = useCallback( async (deprecationDetails: KibanaDeprecationDetails) => { - setDeprecationResolutionState({ - id: deprecationDetails.id, - resolveDeprecationStatus: 'in_progress', + setDeprecationResolutionStates((states) => { + states[deprecationDetails.id] = { + id: deprecationDetails.id, + resolveDeprecationStatus: 'in_progress', + }; + + return states; }); const response = await deprecations.resolveDeprecation(deprecationDetails); - setDeprecationResolutionState({ - id: deprecationDetails.id, - resolveDeprecationStatus: response.status, - resolveDeprecationError: response.status === 'fail' ? response.reason : undefined, + setDeprecationResolutionStates((states) => { + states[deprecationDetails.id] = { + id: deprecationDetails.id, + resolveDeprecationStatus: response.status, + resolveDeprecationError: response.status === 'fail' ? response.reason : undefined, + }; + + return states; }); closeFlyout(); @@ -221,10 +229,7 @@ export const KibanaDeprecationsList = ({ deprecation: flyoutContent, closeFlyout, resolveDeprecation, - deprecationResolutionState: - deprecationResolutionState && flyoutContent.id === deprecationResolutionState.id - ? deprecationResolutionState - : undefined, + deprecationResolutionState: deprecationResolutionStates[flyoutContent.id], }, flyoutProps: { onClose: closeFlyout, @@ -236,7 +241,7 @@ export const KibanaDeprecationsList = ({ }, [ addContentToGlobalFlyout, closeFlyout, - deprecationResolutionState, + deprecationResolutionStates, flyoutContent, resolveDeprecation, ]); @@ -310,7 +315,7 @@ export const KibanaDeprecationsList = ({ deprecations={kibanaDeprecations} reload={getAllDeprecations} toggleFlyout={toggleFlyout} - deprecationResolutionState={deprecationResolutionState} + deprecationResolutionStates={deprecationResolutionStates} />

); diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations_table.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations_table.tsx index 6a757d0cb2b0b..8d223dedca490 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations_table.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations_table.tsx @@ -56,6 +56,12 @@ const i18nTexts = { defaultMessage: 'Feature', } ), + apiDeprecationTypeCellLabel: i18n.translate( + 'xpack.upgradeAssistant.kibanaDeprecations.table.apiDeprecationTypeCellLabel', + { + defaultMessage: 'API', + } + ), unknownDeprecationTypeCellLabel: i18n.translate( 'xpack.upgradeAssistant.kibanaDeprecations.table.unknownDeprecationTypeCellLabel', { @@ -86,14 +92,14 @@ interface Props { deprecations?: KibanaDeprecationDetails[]; reload: () => void; toggleFlyout: (newFlyoutContent?: KibanaDeprecationDetails) => void; - deprecationResolutionState?: DeprecationResolutionState; + deprecationResolutionStates: Record; } export const KibanaDeprecationsTable: React.FunctionComponent = ({ deprecations, reload, toggleFlyout, - deprecationResolutionState, + deprecationResolutionStates, }) => { const columns: Array> = [ { @@ -135,6 +141,8 @@ export const KibanaDeprecationsTable: React.FunctionComponent = ({ return i18nTexts.configDeprecationTypeCellLabel; case 'feature': return i18nTexts.featureDeprecationTypeCellLabel; + case 'api': + return i18nTexts.apiDeprecationTypeCellLabel; case 'uncategorized': default: return i18nTexts.unknownDeprecationTypeCellLabel; @@ -155,7 +163,8 @@ export const KibanaDeprecationsTable: React.FunctionComponent = ({ ); }, @@ -191,6 +200,10 @@ export const KibanaDeprecationsTable: React.FunctionComponent = ({ value: 'feature', name: i18nTexts.featureDeprecationTypeCellLabel, }, + { + value: 'api', + name: i18nTexts.apiDeprecationTypeCellLabel, + }, { value: 'uncategorized', name: i18nTexts.unknownDeprecationTypeCellLabel, diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/resolution_table_cell.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/resolution_table_cell.tsx index 373c9e7b43f52..502c31ae90744 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/resolution_table_cell.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/resolution_table_cell.tsx @@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n'; import type { DeprecationResolutionState } from './kibana_deprecations'; -const i18nTexts = { +const manualI18nTexts = { manualCellLabel: i18n.translate( 'xpack.upgradeAssistant.kibanaDeprecations.table.manualCellLabel', { @@ -32,31 +32,34 @@ const i18nTexts = { defaultMessage: 'This issue needs to be resolved manually.', } ), - automatedCellLabel: i18n.translate( +}; + +const automatedI18nTexts = { + resolutionTypeCellLabel: i18n.translate( 'xpack.upgradeAssistant.kibanaDeprecations.table.automatedCellLabel', { defaultMessage: 'Automated', } ), - automationInProgressCellLabel: i18n.translate( + resolutionProgressCellLabel: i18n.translate( 'xpack.upgradeAssistant.kibanaDeprecations.table.automationInProgressCellLabel', { defaultMessage: 'Resolution in progress…', } ), - automationCompleteCellLabel: i18n.translate( + resolutionCompleteCellLabel: i18n.translate( 'xpack.upgradeAssistant.kibanaDeprecations.table.automationCompleteCellLabel', { defaultMessage: 'Resolved', } ), - automationFailedCellLabel: i18n.translate( + resolutionFailedCellLabel: i18n.translate( 'xpack.upgradeAssistant.kibanaDeprecations.table.automationFailedCellLabel', { defaultMessage: 'Resolution failed', } ), - automatedCellTooltipLabel: i18n.translate( + resolutionCellTooltipLabel: i18n.translate( 'xpack.upgradeAssistant.kibanaDeprecations.table.automatedCellTooltipLabel', { defaultMessage: 'This issue can be resolved automatically.', @@ -64,18 +67,56 @@ const i18nTexts = { ), }; +const markAsResolvedI18nTexts = { + resolutionTypeCellLabel: i18n.translate( + 'xpack.upgradeAssistant.kibanaDeprecations.table.markAsResolvedCellLabel', + { + defaultMessage: 'Mark as resolved', + } + ), + resolutionProgressCellLabel: i18n.translate( + 'xpack.upgradeAssistant.kibanaDeprecations.table.markAsResolvedInProgressCellLabel', + { + defaultMessage: 'Marking as resolved…', + } + ), + resolutionCompleteCellLabel: i18n.translate( + 'xpack.upgradeAssistant.kibanaDeprecations.table.markAsResolvedCompleteCellLabel', + { + defaultMessage: 'Marked as resolved', + } + ), + resolutionFailedCellLabel: i18n.translate( + 'xpack.upgradeAssistant.kibanaDeprecations.table.markAsResolvedFailedCellLabel', + { + defaultMessage: 'Failed to mark as resolved', + } + ), + resolutionCellTooltipLabel: i18n.translate( + 'xpack.upgradeAssistant.kibanaDeprecations.table.markAsResolvedCellTooltipLabel', + { + defaultMessage: 'This issue can be marked as resolved.', + } + ), +}; + interface Props { deprecationId: string; isAutomated: boolean; + canBeMarkedAsResolved: boolean; deprecationResolutionState?: DeprecationResolutionState; } export const ResolutionTableCell: React.FunctionComponent = ({ deprecationId, isAutomated, + canBeMarkedAsResolved, deprecationResolutionState, }) => { - if (isAutomated) { + if (isAutomated || canBeMarkedAsResolved) { + const resolutionI18nTexts = isAutomated ? automatedI18nTexts : markAsResolvedI18nTexts; + const euiIconType = isAutomated ? 'indexSettings' : 'clickLeft'; + if (deprecationResolutionState?.id === deprecationId) { const { resolveDeprecationStatus } = deprecationResolutionState; @@ -87,7 +128,7 @@ export const ResolutionTableCell: React.FunctionComponent = ({ - {i18nTexts.automationInProgressCellLabel} + {resolutionI18nTexts.resolutionProgressCellLabel} ); @@ -98,7 +139,7 @@ export const ResolutionTableCell: React.FunctionComponent = ({ - {i18nTexts.automationFailedCellLabel} + {resolutionI18nTexts.resolutionFailedCellLabel} ); @@ -110,7 +151,7 @@ export const ResolutionTableCell: React.FunctionComponent = ({ - {i18nTexts.automationCompleteCellLabel} + {resolutionI18nTexts.resolutionCompleteCellLabel} ); @@ -118,13 +159,13 @@ export const ResolutionTableCell: React.FunctionComponent = ({ } return ( - + - + - {i18nTexts.automatedCellLabel} + {resolutionI18nTexts.resolutionTypeCellLabel} @@ -134,11 +175,11 @@ export const ResolutionTableCell: React.FunctionComponent = ({ return ( - {i18nTexts.manualCellLabel} + {manualI18nTexts.manualCellLabel} ); diff --git a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts index 8d5caf79a4c89..bf69461b0382a 100644 --- a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts +++ b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts @@ -132,6 +132,7 @@ function getIndexRecordActionType() { secrets, reference: params.reference, source: 'action:test.index-record', + '@timestamp': new Date(), }, }); return { status: 'ok', actionId }; diff --git a/x-pack/test/alerting_api_integration/packages/helpers/es_test_index_tool.ts b/x-pack/test/alerting_api_integration/packages/helpers/es_test_index_tool.ts index 1a84915a5c935..b0de8872e177d 100644 --- a/x-pack/test/alerting_api_integration/packages/helpers/es_test_index_tool.ts +++ b/x-pack/test/alerting_api_integration/packages/helpers/es_test_index_tool.ts @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { omit } from 'lodash'; import type { Client } from '@elastic/elasticsearch'; import { DeleteByQueryRequest } from '@elastic/elasticsearch/lib/api/types'; @@ -61,6 +62,9 @@ export class ESTestIndexTool { group: { type: 'keyword', }, + '@timestamp': { + type: 'date', + }, host: { properties: { hostname: { @@ -109,6 +113,7 @@ export class ESTestIndexTool { async search(source: string, reference?: string) { const body = reference ? { + sort: [{ '@timestamp': 'asc' }], query: { bool: { must: [ @@ -127,6 +132,7 @@ export class ESTestIndexTool { }, } : { + sort: [{ '@timestamp': 'asc' }], query: { term: { source, @@ -138,7 +144,16 @@ export class ESTestIndexTool { size: 1000, body, }; - return await this.es.search(params, { meta: true }); + const result = await this.es.search(params, { meta: true }); + result.body.hits.hits = result.body.hits.hits.map((hit) => { + return { + ...hit, + // Easier to remove @timestamp than to have all the downstream code ignore it + // in their assertions + _source: omit(hit._source as Record, '@timestamp'), + }; + }); + return result; } async getAll(size: number = 10) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/backfill/api_key.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/backfill/api_key.ts index bbb97281b82b1..46a92d176bab0 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/backfill/api_key.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/backfill/api_key.ts @@ -125,7 +125,7 @@ export default function apiKeyBackfillTests({ getService }: FtrProviderContext) } it('should wait to invalidate API key until backfill for rule is complete', async () => { - const start = moment().utc().startOf('day').subtract(7, 'days').toISOString(); + const start = moment().utc().startOf('day').subtract(13, 'days').toISOString(); const end = moment().utc().startOf('day').subtract(4, 'day').toISOString(); const spaceId = SuperuserAtSpace1.space.id; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find.ts index 37d42ceeccb3a..bf5595e5756c1 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find.ts @@ -6,10 +6,8 @@ */ import expect from '@kbn/expect'; -import { Agent as SuperTestAgent } from 'supertest'; import { chunk, omit } from 'lodash'; import { v4 as uuidv4 } from 'uuid'; -import { SupertestWithoutAuthProviderType } from '@kbn/ftr-common-functional-services'; import { ES_QUERY_ID, ML_ANOMALY_DETECTION_RULE_TYPE_ID, @@ -19,13 +17,14 @@ import { SuperuserAtSpace1, UserAtSpaceScenarios, StackAlertsOnly } from '../../ import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../../common/lib'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; -const findTestUtils = ( - describeType: 'internal' | 'public', - objectRemover: ObjectRemover, - supertest: SuperTestAgent, - supertestWithoutAuth: SupertestWithoutAuthProviderType -) => { - describe(describeType, () => { +// eslint-disable-next-line import/no-default-export +export default function createFindTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + describe('find public API', () => { + const objectRemover = new ObjectRemover(supertest); + afterEach(async () => { await objectRemover.removeAll(); }); @@ -71,9 +70,9 @@ const findTestUtils = ( const response = await supertestWithoutAuth .get( - `${getUrlPrefix(space.id)}/${ - describeType === 'public' ? 'api' : 'internal' - }/alerting/rules/_find?search=test.noop&search_fields=alertTypeId` + `${getUrlPrefix( + space.id + )}/api/alerting/rules/_find?search=test.noop&search_fields=alertTypeId` ) .auth(user.username, user.password); @@ -97,8 +96,6 @@ const findTestUtils = ( expect(response.body.per_page).to.be.greaterThan(0); expect(response.body.total).to.be.greaterThan(0); const match = response.body.data.find((obj: any) => obj.id === createdAlert.id); - const activeSnoozes = match.active_snoozes; - const hasActiveSnoozes = !!(activeSnoozes || []).filter((obj: any) => obj).length; expect(match).to.eql({ id: createdAlert.id, name: 'abc', @@ -138,14 +135,6 @@ const findTestUtils = ( execution_status: match.execution_status, ...(match.next_run ? { next_run: match.next_run } : {}), ...(match.last_run ? { last_run: match.last_run } : {}), - ...(describeType === 'internal' - ? { - monitoring: match.monitoring, - snooze_schedule: match.snooze_schedule, - ...(hasActiveSnoozes && { active_snoozes: activeSnoozes }), - is_snoozed_until: null, - } - : {}), }); expect(Date.parse(match.created_at)).to.be.greaterThan(0); expect(Date.parse(match.updated_at)).to.be.greaterThan(0); @@ -195,9 +184,9 @@ const findTestUtils = ( const response = await supertestWithoutAuth .get( - `${getUrlPrefix(space.id)}/${ - describeType === 'public' ? 'api' : 'internal' - }/alerting/rules/_find?per_page=${perPage}&sort_field=createdAt` + `${getUrlPrefix( + space.id + )}/api/alerting/rules/_find?per_page=${perPage}&sort_field=createdAt` ) .auth(user.username, user.password); @@ -244,9 +233,9 @@ const findTestUtils = ( const secondResponse = await supertestWithoutAuth .get( - `${getUrlPrefix(space.id)}/${ - describeType === 'public' ? 'api' : 'internal' - }/alerting/rules/_find?per_page=${perPage}&sort_field=createdAt&page=2` + `${getUrlPrefix( + space.id + )}/api/alerting/rules/_find?per_page=${perPage}&sort_field=createdAt&page=2` ) .auth(user.username, user.password); @@ -291,9 +280,9 @@ const findTestUtils = ( const response = await supertestWithoutAuth .get( - `${getUrlPrefix(space.id)}/${ - describeType === 'public' ? 'api' : 'internal' - }/alerting/rules/_find?filter=alert.attributes.actions:{ actionTypeId: test.noop }` + `${getUrlPrefix( + space.id + )}/api/alerting/rules/_find?filter=alert.attributes.actions:{ actionTypeId: test.noop }` ) .auth(user.username, user.password); @@ -317,8 +306,6 @@ const findTestUtils = ( expect(response.body.per_page).to.be.greaterThan(0); expect(response.body.total).to.be.greaterThan(0); const match = response.body.data.find((obj: any) => obj.id === createdAlert.id); - const activeSnoozes = match.active_snoozes; - const hasActiveSnoozes = !!(activeSnoozes || []).filter((obj: any) => obj).length; expect(match).to.eql({ id: createdAlert.id, name: 'abc', @@ -352,14 +339,6 @@ const findTestUtils = ( revision: 0, ...(match.next_run ? { next_run: match.next_run } : {}), ...(match.last_run ? { last_run: match.last_run } : {}), - ...(describeType === 'internal' - ? { - monitoring: match.monitoring, - snooze_schedule: match.snooze_schedule, - ...(hasActiveSnoozes && { active_snoozes: activeSnoozes }), - is_snoozed_until: null, - } - : {}), }); expect(Date.parse(match.created_at)).to.be.greaterThan(0); expect(Date.parse(match.updated_at)).to.be.greaterThan(0); @@ -401,9 +380,9 @@ const findTestUtils = ( const response = await supertestWithoutAuth .get( - `${getUrlPrefix(space.id)}/${ - describeType === 'public' ? 'api' : 'internal' - }/alerting/rules/_find?filter=alert.attributes.alertTypeId:test.restricted-noop&fields=["tags"]&sort_field=createdAt` + `${getUrlPrefix( + space.id + )}/api/alerting/rules/_find?filter=alert.attributes.alertTypeId:test.restricted-noop&fields=["tags"]&sort_field=createdAt` ) .auth(user.username, user.password); @@ -434,19 +413,11 @@ const findTestUtils = ( id: createdAlert.id, actions: [], tags: [myTag], - ...(describeType === 'internal' && { - snooze_schedule: [], - is_snoozed_until: null, - }), }); expect(omit(matchSecond, 'updatedAt')).to.eql({ id: createdSecondAlert.id, actions: [], tags: [myTag], - ...(describeType === 'internal' && { - snooze_schedule: [], - is_snoozed_until: null, - }), }); break; default: @@ -486,9 +457,9 @@ const findTestUtils = ( const response = await supertestWithoutAuth .get( - `${getUrlPrefix(space.id)}/${ - describeType === 'public' ? 'api' : 'internal' - }/alerting/rules/_find?filter=alert.attributes.alertTypeId:test.restricted-noop&fields=["tags","executionStatus"]&sort_field=createdAt` + `${getUrlPrefix( + space.id + )}/api/alerting/rules/_find?filter=alert.attributes.alertTypeId:test.restricted-noop&fields=["tags","executionStatus"]&sort_field=createdAt` ) .auth(user.username, user.password); @@ -520,20 +491,12 @@ const findTestUtils = ( actions: [], tags: [myTag], execution_status: matchFirst.execution_status, - ...(describeType === 'internal' && { - snooze_schedule: [], - is_snoozed_until: null, - }), }); expect(omit(matchSecond, 'updatedAt')).to.eql({ id: createdSecondAlert.id, actions: [], tags: [myTag], execution_status: matchSecond.execution_status, - ...(describeType === 'internal' && { - snooze_schedule: [], - is_snoozed_until: null, - }), }); break; default: @@ -551,9 +514,9 @@ const findTestUtils = ( const response = await supertestWithoutAuth .get( - `${getUrlPrefix('other')}/${ - describeType === 'public' ? 'api' : 'internal' - }/alerting/rules/_find?search=test.noop&search_fields=alertTypeId` + `${getUrlPrefix( + 'other' + )}/api/alerting/rules/_find?search=test.noop&search_fields=alertTypeId` ) .auth(user.username, user.password); @@ -586,88 +549,71 @@ const findTestUtils = ( }); }); } - }); - describe('Actions', () => { - const { user, space } = SuperuserAtSpace1; - - it('should return the actions correctly', async () => { - const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/actions/connector`) - .set('kbn-xsrf', 'foo') - .send({ - name: 'MY action', - connector_type_id: 'test.noop', - config: {}, - secrets: {}, - }) - .expect(200); - - const { body: createdRule1 } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send( - getTestRuleData({ - enabled: true, - actions: [ - { - id: createdAction.id, - group: 'default', - params: {}, - }, - { - id: 'system-connector-test.system-action', - params: {}, - }, - ], + describe('Actions', () => { + const { user, space } = SuperuserAtSpace1; + + it('should return the actions correctly', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'MY action', + connector_type_id: 'test.noop', + config: {}, + secrets: {}, }) - ) - .expect(200); - - objectRemover.add(space.id, createdRule1.id, 'rule', 'alerting'); - - const response = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerting/rules/_find`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password); - - const action = response.body.data[0].actions[0]; - const systemAction = response.body.data[0].actions[1]; - const { uuid, ...restAction } = action; - const { uuid: systemActionUuid, ...restSystemAction } = systemAction; - - expect([restAction, restSystemAction]).to.eql([ - { - id: createdAction.id, - connector_type_id: 'test.noop', - group: 'default', - params: {}, - }, - { - id: 'system-connector-test.system-action', - connector_type_id: 'test.system-action', - params: {}, - }, - , - ]); - }); - }); -}; + .expect(200); -// eslint-disable-next-line import/no-default-export -export default function createFindTests({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); + const { body: createdRule1 } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + enabled: true, + actions: [ + { + id: createdAction.id, + group: 'default', + params: {}, + }, + { + id: 'system-connector-test.system-action', + params: {}, + }, + ], + }) + ) + .expect(200); - describe('find', () => { - const objectRemover = new ObjectRemover(supertest); + objectRemover.add(space.id, createdRule1.id, 'rule', 'alerting'); - afterEach(async () => { - await objectRemover.removeAll(); - }); + const response = await supertestWithoutAuth + .get(`${getUrlPrefix(space.id)}/api/alerting/rules/_find`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password); + + const action = response.body.data[0].actions[0]; + const systemAction = response.body.data[0].actions[1]; + const { uuid, ...restAction } = action; + const { uuid: systemActionUuid, ...restSystemAction } = systemAction; - findTestUtils('public', objectRemover, supertest, supertestWithoutAuth); - findTestUtils('internal', objectRemover, supertest, supertestWithoutAuth); + expect([restAction, restSystemAction]).to.eql([ + { + id: createdAction.id, + connector_type_id: 'test.noop', + group: 'default', + params: {}, + }, + { + id: 'system-connector-test.system-action', + connector_type_id: 'test.system-action', + params: {}, + }, + , + ]); + }); + }); describe('stack alerts', () => { const ruleTypes = [ diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find_internal.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find_internal.ts new file mode 100644 index 0000000000000..ee8d49c662ada --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find_internal.ts @@ -0,0 +1,762 @@ +/* + * Copyright 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 expect from '@kbn/expect'; +import { chunk, omit } from 'lodash'; +import { v4 as uuidv4 } from 'uuid'; +import { + ES_QUERY_ID, + ML_ANOMALY_DETECTION_RULE_TYPE_ID, + OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, +} from '@kbn/rule-data-utils'; +import { SuperuserAtSpace1, UserAtSpaceScenarios, StackAlertsOnly } from '../../../scenarios'; +import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function createFindTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + describe('find internal API', () => { + const objectRemover = new ObjectRemover(supertest); + + afterEach(async () => { + await objectRemover.removeAll(); + }); + + for (const scenario of UserAtSpaceScenarios) { + const { user, space } = scenario; + describe(scenario.id, () => { + it('should handle find alert request appropriately', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'MY action', + connector_type_id: 'test.noop', + config: {}, + secrets: {}, + }) + .expect(200); + + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + actions: [ + { + group: 'default', + id: createdAction.id, + params: {}, + frequency: { + summary: false, + notify_when: 'onThrottleInterval', + throttle: '1m', + }, + }, + ], + notify_when: undefined, + throttle: undefined, + }) + ) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/internal/alerting/rules/_find`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send({ + search: 'test.noop', + search_fields: 'alertTypeId', + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: `Unauthorized to find rules for any rule types`, + statusCode: 403, + }); + break; + case 'global_read at space1': + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + expect(response.body.page).to.equal(1); + expect(response.body.per_page).to.be.greaterThan(0); + expect(response.body.total).to.be.greaterThan(0); + const match = response.body.data.find((obj: any) => obj.id === createdAlert.id); + const activeSnoozes = match.active_snoozes; + const hasActiveSnoozes = !!(activeSnoozes || []).filter((obj: any) => obj).length; + expect(match).to.eql({ + id: createdAlert.id, + name: 'abc', + tags: ['foo'], + rule_type_id: 'test.noop', + running: match.running ?? false, + consumer: 'alertsFixture', + schedule: { interval: '1m' }, + enabled: true, + actions: [ + { + group: 'default', + id: createdAction.id, + connector_type_id: 'test.noop', + params: {}, + uuid: match.actions[0].uuid, + frequency: { + summary: false, + notify_when: 'onThrottleInterval', + throttle: '1m', + }, + }, + ], + params: {}, + created_by: 'elastic', + scheduled_task_id: match.scheduled_task_id, + created_at: match.created_at, + updated_at: match.updated_at, + throttle: null, + notify_when: null, + updated_by: 'elastic', + api_key_owner: 'elastic', + api_key_created_by_user: false, + mute_all: false, + muted_alert_ids: [], + revision: 0, + execution_status: match.execution_status, + ...(match.next_run ? { next_run: match.next_run } : {}), + ...(match.last_run ? { last_run: match.last_run } : {}), + + monitoring: match.monitoring, + snooze_schedule: match.snooze_schedule, + ...(hasActiveSnoozes && { active_snoozes: activeSnoozes }), + is_snoozed_until: null, + }); + expect(Date.parse(match.created_at)).to.be.greaterThan(0); + expect(Date.parse(match.updated_at)).to.be.greaterThan(0); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should filter out types that the user is not authorized to `get` retaining pagination', async () => { + async function createNoOpAlert(overrides = {}) { + const alert = getTestRuleData(overrides); + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(alert) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + return { + id: createdAlert.id, + rule_type_id: alert.rule_type_id, + }; + } + function createRestrictedNoOpAlert() { + return createNoOpAlert({ + rule_type_id: 'test.restricted-noop', + consumer: 'alertsRestrictedFixture', + }); + } + function createUnrestrictedNoOpAlert() { + return createNoOpAlert({ + rule_type_id: 'test.unrestricted-noop', + consumer: 'alertsFixture', + }); + } + const allAlerts = []; + allAlerts.push(await createNoOpAlert()); + allAlerts.push(await createNoOpAlert()); + allAlerts.push(await createRestrictedNoOpAlert()); + allAlerts.push(await createUnrestrictedNoOpAlert()); + allAlerts.push(await createUnrestrictedNoOpAlert()); + allAlerts.push(await createRestrictedNoOpAlert()); + allAlerts.push(await createNoOpAlert()); + allAlerts.push(await createNoOpAlert()); + + const perPage = 4; + + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/internal/alerting/rules/_find`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send({ + per_page: perPage, + sort_field: 'createdAt', + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: `Unauthorized to find rules for any rule types`, + statusCode: 403, + }); + break; + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(200); + expect(response.body.page).to.equal(1); + expect(response.body.per_page).to.be.equal(perPage); + expect(response.body.total).to.be.equal(6); + { + const [firstPage] = chunk( + allAlerts + .filter((alert) => alert.rule_type_id !== 'test.restricted-noop') + .map((alert) => alert.id), + perPage + ); + expect(response.body.data.map((alert: any) => alert.id)).to.eql(firstPage); + } + break; + case 'global_read at space1': + case 'superuser at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + expect(response.body.page).to.equal(1); + expect(response.body.per_page).to.be.equal(perPage); + expect(response.body.total).to.be.equal(8); + + { + const [firstPage, secondPage] = chunk( + allAlerts.map((alert) => alert.id), + perPage + ); + expect(response.body.data.map((alert: any) => alert.id)).to.eql(firstPage); + + const secondResponse = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/internal/alerting/rules/_find`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send({ + per_page: perPage, + sort_field: 'createdAt', + page: 2, + }); + + expect(secondResponse.body.data.map((alert: any) => alert.id)).to.eql(secondPage); + } + + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle find alert request with filter appropriately', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action', + connector_type_id: 'test.noop', + config: {}, + secrets: {}, + }) + .expect(200); + + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + enabled: false, + actions: [ + { + id: createdAction.id, + group: 'default', + params: {}, + }, + ], + }) + ) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/internal/alerting/rules/_find`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send({ + filter: 'alert.attributes.actions:{ actionTypeId: test.noop }', + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: `Unauthorized to find rules for any rule types`, + statusCode: 403, + }); + break; + case 'global_read at space1': + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + expect(response.body.page).to.equal(1); + expect(response.body.per_page).to.be.greaterThan(0); + expect(response.body.total).to.be.greaterThan(0); + const match = response.body.data.find((obj: any) => obj.id === createdAlert.id); + const activeSnoozes = match.active_snoozes; + const hasActiveSnoozes = !!(activeSnoozes || []).filter((obj: any) => obj).length; + expect(match).to.eql({ + id: createdAlert.id, + name: 'abc', + tags: ['foo'], + rule_type_id: 'test.noop', + running: match.running ?? false, + consumer: 'alertsFixture', + schedule: { interval: '1m' }, + enabled: false, + actions: [ + { + id: createdAction.id, + group: 'default', + connector_type_id: 'test.noop', + params: {}, + uuid: createdAlert.actions[0].uuid, + }, + ], + params: {}, + created_by: 'elastic', + throttle: '1m', + api_key_created_by_user: null, + updated_by: 'elastic', + api_key_owner: null, + mute_all: false, + muted_alert_ids: [], + notify_when: 'onThrottleInterval', + created_at: match.created_at, + updated_at: match.updated_at, + execution_status: match.execution_status, + revision: 0, + ...(match.next_run ? { next_run: match.next_run } : {}), + ...(match.last_run ? { last_run: match.last_run } : {}), + monitoring: match.monitoring, + snooze_schedule: match.snooze_schedule, + ...(hasActiveSnoozes && { active_snoozes: activeSnoozes }), + is_snoozed_until: null, + }); + expect(Date.parse(match.created_at)).to.be.greaterThan(0); + expect(Date.parse(match.updated_at)).to.be.greaterThan(0); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle find alert request with fields appropriately', async () => { + const myTag = uuidv4(); + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + enabled: false, + tags: [myTag], + rule_type_id: 'test.restricted-noop', + consumer: 'alertsRestrictedFixture', + }) + ) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + + // create another type with same tag + const { body: createdSecondAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + tags: [myTag], + rule_type_id: 'test.restricted-noop', + consumer: 'alertsRestrictedFixture', + }) + ) + .expect(200); + objectRemover.add(space.id, createdSecondAlert.id, 'rule', 'alerting'); + + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/internal/alerting/rules/_find`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send({ + filter: 'alert.attributes.alertTypeId:test.restricted-noop', + fields: ['tags'], + sort_field: 'createdAt', + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: `Unauthorized to find rules for any rule types`, + statusCode: 403, + }); + break; + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(200); + expect(response.body.data).to.eql([]); + break; + case 'global_read at space1': + case 'superuser at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + expect(response.body.page).to.equal(1); + expect(response.body.per_page).to.be.greaterThan(0); + expect(response.body.total).to.be.greaterThan(0); + const [matchFirst, matchSecond] = response.body.data; + expect(omit(matchFirst, 'updatedAt')).to.eql({ + id: createdAlert.id, + actions: [], + tags: [myTag], + snooze_schedule: [], + is_snoozed_until: null, + }); + expect(omit(matchSecond, 'updatedAt')).to.eql({ + id: createdSecondAlert.id, + actions: [], + tags: [myTag], + snooze_schedule: [], + is_snoozed_until: null, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle find alert request with executionStatus field appropriately', async () => { + const myTag = uuidv4(); + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + enabled: false, + tags: [myTag], + rule_type_id: 'test.restricted-noop', + consumer: 'alertsRestrictedFixture', + }) + ) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + + // create another type with same tag + const { body: createdSecondAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + tags: [myTag], + rule_type_id: 'test.restricted-noop', + consumer: 'alertsRestrictedFixture', + }) + ) + .expect(200); + objectRemover.add(space.id, createdSecondAlert.id, 'rule', 'alerting'); + + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/internal/alerting/rules/_find`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send({ + filter: 'alert.attributes.alertTypeId:test.restricted-noop', + fields: ['tags', 'executionStatus'], + sort_field: 'createdAt', + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: `Unauthorized to find rules for any rule types`, + statusCode: 403, + }); + break; + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(200); + expect(response.body.data).to.eql([]); + break; + case 'global_read at space1': + case 'superuser at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + expect(response.body.page).to.equal(1); + expect(response.body.per_page).to.be.greaterThan(0); + expect(response.body.total).to.be.greaterThan(0); + const [matchFirst, matchSecond] = response.body.data; + expect(omit(matchFirst, 'updatedAt')).to.eql({ + id: createdAlert.id, + actions: [], + tags: [myTag], + execution_status: matchFirst.execution_status, + snooze_schedule: [], + is_snoozed_until: null, + }); + expect(omit(matchSecond, 'updatedAt')).to.eql({ + id: createdSecondAlert.id, + actions: [], + tags: [myTag], + execution_status: matchSecond.execution_status, + snooze_schedule: [], + is_snoozed_until: null, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it(`shouldn't find alert from another space`, async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + + const response = await supertestWithoutAuth + .post(`${getUrlPrefix('other')}/internal/alerting/rules/_find`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send({ + search: 'test.noop', + search_fields: 'alertTypeId', + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: `Unauthorized to find rules for any rule types`, + statusCode: 403, + }); + break; + case 'global_read at space1': + case 'superuser at space1': + expect(response.statusCode).to.eql(200); + expect(response.body).to.eql({ + page: 1, + per_page: 10, + total: 0, + data: [], + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + }); + } + + describe('Actions', () => { + const { user, space } = SuperuserAtSpace1; + + it('should return the actions correctly', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'MY action', + connector_type_id: 'test.noop', + config: {}, + secrets: {}, + }) + .expect(200); + + const { body: createdRule1 } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + enabled: true, + actions: [ + { + id: createdAction.id, + group: 'default', + params: {}, + }, + { + id: 'system-connector-test.system-action', + params: {}, + }, + ], + }) + ) + .expect(200); + + objectRemover.add(space.id, createdRule1.id, 'rule', 'alerting'); + + const response = await supertestWithoutAuth + .get(`${getUrlPrefix(space.id)}/api/alerting/rules/_find`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password); + + const action = response.body.data[0].actions[0]; + const systemAction = response.body.data[0].actions[1]; + const { uuid, ...restAction } = action; + const { uuid: systemActionUuid, ...restSystemAction } = systemAction; + + expect([restAction, restSystemAction]).to.eql([ + { + id: createdAction.id, + connector_type_id: 'test.noop', + group: 'default', + params: {}, + }, + { + id: 'system-connector-test.system-action', + connector_type_id: 'test.system-action', + params: {}, + }, + , + ]); + }); + }); + describe('stack alerts', () => { + const ruleTypes = [ + [ + ES_QUERY_ID, + { + searchType: 'esQuery', + timeWindowSize: 5, + timeWindowUnit: 'm', + threshold: [1000], + thresholdComparator: '>', + size: 100, + esQuery: '{\n "query":{\n "match_all" : {}\n }\n }', + aggType: 'count', + groupBy: 'all', + termSize: 5, + excludeHitsFromPreviousRun: false, + sourceFields: [], + index: ['.kibana'], + timeField: 'created_at', + }, + ], + [ + OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, + { + criteria: [ + { + comparator: '>', + metrics: [ + { + name: 'A', + aggType: 'count', + }, + ], + threshold: [100], + timeSize: 1, + timeUnit: 'm', + }, + ], + alertOnNoData: false, + alertOnGroupDisappear: false, + searchConfiguration: { + query: { + query: '', + language: 'kuery', + }, + index: 'kibana-event-log-data-view', + }, + }, + ], + [ + ML_ANOMALY_DETECTION_RULE_TYPE_ID, + { + severity: 75, + resultType: 'bucket', + includeInterim: false, + jobSelection: { + jobIds: ['low_request_rate'], + }, + }, + ], + ]; + + const createRule = async (rule = {}) => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix('space1')}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData({ ...rule })) + .expect(200); + + objectRemover.add('space1', createdAlert.id, 'rule', 'alerting'); + }; + + for (const [ruleTypeId, params] of ruleTypes) { + it(`should get rules of ${ruleTypeId} rule type ID and stackAlerts consumer`, async () => { + /** + * We create two rules. The first one is a test.noop + * rule with stackAlerts as consumer. The second rule + * is has different rule type ID but with the same consumer as the first rule (stackAlerts). + * This way we can verify that the find API call returns only the rules the user is authorized to. + * Specifically only the second rule because the StackAlertsOnly user does not have + * access to the test.noop rule type. + */ + await createRule({ consumer: 'stackAlerts' }); + await createRule({ rule_type_id: ruleTypeId, params, consumer: 'stackAlerts' }); + + const response = await supertestWithoutAuth + .get(`${getUrlPrefix('space1')}/api/alerting/rules/_find`) + .auth(StackAlertsOnly.username, StackAlertsOnly.password); + + expect(response.statusCode).to.eql(200); + expect(response.body.total).to.equal(1); + expect(response.body.data[0].rule_type_id).to.equal(ruleTypeId); + expect(response.body.data[0].consumer).to.equal('stackAlerts'); + }); + } + + for (const [ruleTypeId, params] of ruleTypes) { + it(`should NOT get rules of ${ruleTypeId} rule type ID and NOT stackAlerts consumer`, async () => { + /** + * We create two rules with logs as consumer. The user is authorized to + * access rules only with the stackAlerts consumers. + */ + await createRule({ consumer: 'logs' }); + await createRule({ rule_type_id: ruleTypeId, params, consumer: 'logs' }); + + const response = await supertestWithoutAuth + .get(`${getUrlPrefix('space1')}/api/alerting/rules/_find`) + .auth(StackAlertsOnly.username, StackAlertsOnly.password); + + expect(response.statusCode).to.eql(200); + expect(response.body.total).to.equal(0); + }); + } + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find_with_post.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find_with_post.ts index f221b5869dd6b..c8afff8fcc476 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find_with_post.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find_with_post.ts @@ -207,12 +207,14 @@ const findTestUtils = ( expect(response.body.data.map((alert: any) => alert.id)).to.eql(firstPage); const secondResponse = await supertestWithoutAuth - .get( - `${getUrlPrefix(space.id)}/${ - describeType === 'public' ? 'api' : 'internal' - }/alerting/rules/_find?per_page=${perPage}&sort_field=createdAt&page=2` - ) - .auth(user.username, user.password); + .post(`${getUrlPrefix(space.id)}/internal/alerting/rules/_find`) + .set('kbn-xsrf', 'kibana') + .auth(user.username, user.password) + .send({ + per_page: perPage, + sort_field: 'createdAt', + page: 2, + }); expect(secondResponse.body.data.map((alert: any) => alert.id)).to.eql(secondPage); } diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/index.ts index ec938e8dc4abb..262fe8af301c8 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/index.ts @@ -22,6 +22,7 @@ export default function alertingTests({ loadTestFile, getService }: FtrProviderC loadTestFile(require.resolve('./backfill')); loadTestFile(require.resolve('./find')); + loadTestFile(require.resolve('./find_internal')); loadTestFile(require.resolve('./find_with_post')); loadTestFile(require.resolve('./create')); loadTestFile(require.resolve('./delete')); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group4/tests/alerting/alerts.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group4/tests/alerting/alerts.ts index 9d3cac9ef9a6d..78213729efdf8 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group4/tests/alerting/alerts.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group4/tests/alerting/alerts.ts @@ -1184,7 +1184,7 @@ instanceStateValue: true reference, overwrites: { enabled: false, - schedule: { interval: '1s' }, + schedule: { interval: '1m' }, }, }); @@ -1288,7 +1288,7 @@ instanceStateValue: true ); // @ts-expect-error doesnt handle total: number - expect(searchResult.body.hits.total.value).to.eql(1); + expect(searchResult.body.hits.total.value).to.be.greaterThan(0); // @ts-expect-error _source: unknown expect(searchResult.body.hits.hits[0]._source.params.message).to.eql( 'Alerts, all:2, new:2 IDs:[1,2,], ongoing:0 IDs:[], recovered:0 IDs:[]' @@ -1304,7 +1304,7 @@ instanceStateValue: true const response = await alertUtils.createAlwaysFiringRuleWithSummaryAction({ reference, overwrites: { - schedule: { interval: '1s' }, + schedule: { interval: '1h' }, }, notifyWhen: 'onActiveAlert', throttle: null, @@ -1435,7 +1435,7 @@ instanceStateValue: true const response = await alertUtils.createAlwaysFiringRuleWithSummaryAction({ reference, overwrites: { - schedule: { interval: '1s' }, + schedule: { interval: '3s' }, }, notifyWhen: 'onThrottleInterval', throttle: '10s', diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/create.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/create.ts index 9932ee2c11a36..57d41424186b3 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/create.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/create.ts @@ -341,13 +341,14 @@ export default function createAlertTests({ getService }: FtrProviderContext) { ) .expect(200); - const response = await supertest.get( - `${getUrlPrefix( - Spaces.space1.id - )}/internal/alerting/rules/_find?filter=alert.attributes.params.risk_score:40` - ); + const response = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/_find`) + .set('kbn-xsrf', 'kibana') + .send({ + filter: `alert.attributes.params.risk_score:40`, + }) + .expect(200); - expect(response.status).to.eql(200); objectRemover.add(Spaces.space1.id, createResponse.body.id, 'rule', 'alerting'); expect(response.body.total).to.equal(1); expect(response.body.data[0].mapped_params).to.eql({ diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/event_log.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/event_log.ts index e25d64e509101..e3023a0d6c8f7 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/event_log.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/event_log.ts @@ -82,8 +82,8 @@ export default function eventLogTests({ getService }: FtrProviderContext) { .send( getTestRuleData({ rule_type_id: 'test.patternFiring', - schedule: { interval: '1s' }, - throttle: null, + schedule: { interval: '2s' }, + throttle: '1s', params: { pattern, }, @@ -665,6 +665,10 @@ export default function eventLogTests({ getService }: FtrProviderContext) { status_change_threshold: 4, }) .expect(200); + + // wait so cache expires + await setTimeoutAsync(TEST_CACHE_EXPIRATION_TIME); + const { body: createdAction } = await supertest .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') @@ -763,6 +767,10 @@ export default function eventLogTests({ getService }: FtrProviderContext) { status_change_threshold: 4, }) .expect(200); + + // wait so cache expires + await setTimeoutAsync(TEST_CACHE_EXPIRATION_TIME); + const { body: createdAction } = await supertest .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') @@ -871,6 +879,10 @@ export default function eventLogTests({ getService }: FtrProviderContext) { status_change_threshold: 4, }) .expect(200); + + // wait so cache expires + await setTimeoutAsync(TEST_CACHE_EXPIRATION_TIME); + const { body: createdAction } = await supertest .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') @@ -964,6 +976,10 @@ export default function eventLogTests({ getService }: FtrProviderContext) { status_change_threshold: 4, }) .expect(200); + + // wait so cache expires + await setTimeoutAsync(TEST_CACHE_EXPIRATION_TIME); + const { body: createdAction } = await supertest .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') @@ -1067,6 +1083,10 @@ export default function eventLogTests({ getService }: FtrProviderContext) { status_change_threshold: 5, }) .expect(200); + + // wait so cache expires + await setTimeoutAsync(TEST_CACHE_EXPIRATION_TIME); + const { body: createdAction } = await supertest .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') @@ -1166,6 +1186,10 @@ export default function eventLogTests({ getService }: FtrProviderContext) { status_change_threshold: 4, }) .expect(200); + + // wait so cache expires + await setTimeoutAsync(TEST_CACHE_EXPIRATION_TIME); + const { body: createdAction } = await supertest .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') @@ -1192,7 +1216,8 @@ export default function eventLogTests({ getService }: FtrProviderContext) { .send( getTestRuleData({ rule_type_id: 'test.patternFiring', - schedule: { interval: '1s' }, + schedule: { interval: '2s' }, + notify_when: RuleNotifyWhen.THROTTLE, throttle: '1s', params: { pattern, @@ -1263,6 +1288,10 @@ export default function eventLogTests({ getService }: FtrProviderContext) { status_change_threshold: 4, }) .expect(200); + + // wait so cache expires + await setTimeoutAsync(TEST_CACHE_EXPIRATION_TIME); + const { body: createdAction } = await supertest .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') @@ -1289,7 +1318,7 @@ export default function eventLogTests({ getService }: FtrProviderContext) { .send( getTestRuleData({ rule_type_id: 'test.patternFiring', - schedule: { interval: '1s' }, + schedule: { interval: '2s' }, throttle: null, notify_when: null, params: { @@ -1302,8 +1331,7 @@ export default function eventLogTests({ getService }: FtrProviderContext) { params: {}, frequency: { summary: false, - throttle: '1s', - notify_when: RuleNotifyWhen.THROTTLE, + notify_when: RuleNotifyWhen.ACTIVE, }, }, { @@ -1312,8 +1340,7 @@ export default function eventLogTests({ getService }: FtrProviderContext) { params: {}, frequency: { summary: false, - throttle: '1s', - notify_when: RuleNotifyWhen.THROTTLE, + notify_when: RuleNotifyWhen.ACTIVE, }, }, ], @@ -1371,6 +1398,10 @@ export default function eventLogTests({ getService }: FtrProviderContext) { status_change_threshold: 4, }) .expect(200); + + // wait so cache expires + await setTimeoutAsync(TEST_CACHE_EXPIRATION_TIME); + const { body: createdAction } = await supertest .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') @@ -1396,7 +1427,7 @@ export default function eventLogTests({ getService }: FtrProviderContext) { .send( getTestRuleData({ rule_type_id: 'test.patternFiring', - schedule: { interval: '1s' }, + schedule: { interval: '2s' }, throttle: '1s', params: { pattern, @@ -1463,6 +1494,10 @@ export default function eventLogTests({ getService }: FtrProviderContext) { status_change_threshold: 4, }) .expect(200); + + // wait so cache expires + await setTimeoutAsync(TEST_CACHE_EXPIRATION_TIME); + const { body: createdAction } = await supertest .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') @@ -1488,7 +1523,7 @@ export default function eventLogTests({ getService }: FtrProviderContext) { .send( getTestRuleData({ rule_type_id: 'test.patternFiring', - schedule: { interval: '1s' }, + schedule: { interval: '2s' }, throttle: null, notify_when: null, params: { @@ -1501,8 +1536,7 @@ export default function eventLogTests({ getService }: FtrProviderContext) { params: {}, frequency: { summary: false, - throttle: '1s', - notify_when: RuleNotifyWhen.THROTTLE, + notify_when: RuleNotifyWhen.ACTIVE, }, }, { @@ -1511,8 +1545,7 @@ export default function eventLogTests({ getService }: FtrProviderContext) { params: {}, frequency: { summary: false, - throttle: '1s', - notify_when: RuleNotifyWhen.THROTTLE, + notify_when: RuleNotifyWhen.ACTIVE, }, }, ], @@ -1567,6 +1600,9 @@ export default function eventLogTests({ getService }: FtrProviderContext) { }) .expect(200); + // wait so cache expires + await setTimeoutAsync(TEST_CACHE_EXPIRATION_TIME); + // flap and then recover, then active again const instance = [true, false, true, false, true].concat( ...new Array(6).fill(false), @@ -1709,8 +1745,8 @@ export default function eventLogTests({ getService }: FtrProviderContext) { .send( getTestRuleData({ rule_type_id: 'test.patternFiring', - schedule: { interval: '1s' }, - throttle: null, + schedule: { interval: '2s' }, + throttle: '1s', params: { pattern, }, @@ -1942,8 +1978,8 @@ export default function eventLogTests({ getService }: FtrProviderContext) { provider: 'alerting', actions: new Map([ // make sure the counts of the # of events per type are as expected - ['execute-start', { equal: 6 }], - ['execute', { equal: 6 }], + ['execute-start', { gte: 6 }], + ['execute', { gte: 6 }], ['new-instance', { equal: 1 }], ['active-instance', { equal: 2 }], ['recovered-instance', { equal: 1 }], diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/find.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/find.ts index c8aeba2b7e210..e730dd657d2f1 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/find.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/find.ts @@ -26,13 +26,15 @@ async function createAlert( return createdAlert; } -const findTestUtils = ( - describeType: 'internal' | 'public', - supertest: SuperTestAgent, - objectRemover: ObjectRemover -) => { - describe(describeType, () => { +// eslint-disable-next-line import/no-default-export +export default function createFindTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + describe('find public API', () => { + const objectRemover = new ObjectRemover(supertest); + afterEach(() => objectRemover.removeAll()); + describe('handle find alert request', function () { this.tags('skipFIPS'); it('should handle find alert request appropriately', async () => { @@ -72,9 +74,9 @@ const findTestUtils = ( objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/${ - describeType === 'public' ? 'api' : 'internal' - }/alerting/rules/_find?search=test.noop&search_fields=alertTypeId` + `${getUrlPrefix( + Spaces.space1.id + )}/api/alerting/rules/_find?search=test.noop&search_fields=alertTypeId` ); expect(response.status).to.eql(200); @@ -82,8 +84,6 @@ const findTestUtils = ( expect(response.body.per_page).to.be.greaterThan(0); expect(response.body.total).to.be.greaterThan(0); const match = response.body.data.find((obj: any) => obj.id === createdAlert.id); - const activeSnoozes = match.active_snoozes; - const hasActiveSnoozes = !!(activeSnoozes || []).filter((obj: any) => obj).length; expect(match).to.eql({ id: createdAlert.id, name: 'abc', @@ -123,14 +123,6 @@ const findTestUtils = ( execution_status: match.execution_status, ...(match.next_run ? { next_run: match.next_run } : {}), ...(match.last_run ? { last_run: match.last_run } : {}), - ...(describeType === 'internal' - ? { - monitoring: match.monitoring, - snooze_schedule: match.snooze_schedule, - ...(hasActiveSnoozes && { active_snoozes: activeSnoozes }), - is_snoozed_until: null, - } - : {}), }); expect(Date.parse(match.created_at)).to.be.greaterThan(0); expect(Date.parse(match.updated_at)).to.be.greaterThan(0); @@ -147,9 +139,9 @@ const findTestUtils = ( await supertest .get( - `${getUrlPrefix(Spaces.other.id)}/${ - describeType === 'public' ? 'api' : 'internal' - }/alerting/rules/_find?search=test.noop&search_fields=alertTypeId` + `${getUrlPrefix( + Spaces.other.id + )}/api/alerting/rules/_find?search=test.noop&search_fields=alertTypeId` ) .expect(200, { page: 1, @@ -186,62 +178,50 @@ const findTestUtils = ( ]); }); - it(`it should${ - describeType === 'public' ? ' NOT' : '' - } allow filter on monitoring attributes`, async () => { + it(`it should NOT allow filter on monitoring attributes`, async () => { const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/${ - describeType === 'public' ? 'api' : 'internal' - }/alerting/rules/_find?filter=alert.attributes.monitoring.run.calculated_metrics.success_ratio>50` + `${getUrlPrefix( + Spaces.space1.id + )}/api/alerting/rules/_find?filter=alert.attributes.monitoring.run.calculated_metrics.success_ratio>50` ); - expect(response.status).to.eql(describeType === 'internal' ? 200 : 400); - if (describeType === 'public') { - expect(response.body.message).to.eql( - 'Error find rules: Filter is not supported on this field alert.attributes.monitoring.run.calculated_metrics.success_ratio' - ); - } + expect(response.status).to.eql(400); + expect(response.body.message).to.eql( + 'Error find rules: Filter is not supported on this field alert.attributes.monitoring.run.calculated_metrics.success_ratio' + ); }); - it(`it should${ - describeType === 'public' ? ' NOT' : '' - } allow ordering on monitoring attributes`, async () => { + it(`it should NOT allow ordering on monitoring attributes`, async () => { const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/${ - describeType === 'public' ? 'api' : 'internal' - }/alerting/rules/_find?sort_field=monitoring.run.calculated_metrics.success_ratio` + `${getUrlPrefix( + Spaces.space1.id + )}/api/alerting/rules/_find?sort_field=monitoring.run.calculated_metrics.success_ratio` ); - expect(response.status).to.eql(describeType === 'internal' ? 200 : 400); - if (describeType === 'public') { - expect(response.body.message).to.eql( - 'Error find rules: Sort is not supported on this field monitoring.run.calculated_metrics.success_ratio' - ); - } + expect(response.status).to.eql(400); + expect(response.body.message).to.eql( + 'Error find rules: Sort is not supported on this field monitoring.run.calculated_metrics.success_ratio' + ); }); - it(`it should${ - describeType === 'public' ? ' NOT' : '' - } allow search_fields on monitoring attributes`, async () => { + it(`it should NOT allow search_fields on monitoring attributes`, async () => { const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/${ - describeType === 'public' ? 'api' : 'internal' - }/alerting/rules/_find?search_fields=monitoring.run.calculated_metrics.success_ratio&search=50` + `${getUrlPrefix( + Spaces.space1.id + )}/api/alerting/rules/_find?search_fields=monitoring.run.calculated_metrics.success_ratio&search=50` ); - expect(response.status).to.eql(describeType === 'internal' ? 200 : 400); - if (describeType === 'public') { - expect(response.body.message).to.eql( - 'Error find rules: Search field monitoring.run.calculated_metrics.success_ratio not supported' - ); - } + expect(response.status).to.eql(400); + expect(response.body.message).to.eql( + 'Error find rules: Search field monitoring.run.calculated_metrics.success_ratio not supported' + ); }); it('should filter on string parameters', async () => { const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/${ - describeType === 'public' ? 'api' : 'internal' - }/alerting/rules/_find?filter=alert.attributes.params.strValue:"my b"` + `${getUrlPrefix( + Spaces.space1.id + )}/api/alerting/rules/_find?filter=alert.attributes.params.strValue:"my b"` ); expect(response.status).to.eql(200); @@ -251,9 +231,7 @@ const findTestUtils = ( it('should filter on kueryNode parameters', async () => { const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/${ - describeType === 'public' ? 'api' : 'internal' - }/alerting/rules/_find?filter=${JSON.stringify( + `${getUrlPrefix(Spaces.space1.id)}/api/alerting/rules/_find?filter=${JSON.stringify( fromKueryExpression('alert.attributes.params.strValue:"my b"') )}` ); @@ -265,9 +243,9 @@ const findTestUtils = ( it('should sort by parameters', async () => { const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/${ - describeType === 'public' ? 'api' : 'internal' - }/alerting/rules/_find?sort_field=params.severity&sort_order=asc` + `${getUrlPrefix( + Spaces.space1.id + )}/api/alerting/rules/_find?sort_field=params.severity&sort_order=asc` ); expect(response.body.data[0].params.severity).to.equal('low'); expect(response.body.data[1].params.severity).to.equal('medium'); @@ -276,9 +254,9 @@ const findTestUtils = ( it('should search by parameters', async () => { const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/${ - describeType === 'public' ? 'api' : 'internal' - }/alerting/rules/_find?search_fields=params.severity&search=medium` + `${getUrlPrefix( + Spaces.space1.id + )}/api/alerting/rules/_find?search_fields=params.severity&search=medium` ); expect(response.status).to.eql(200); @@ -288,51 +266,31 @@ const findTestUtils = ( it('should filter on parameters', async () => { const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/${ - describeType === 'public' ? 'api' : 'internal' - }/alerting/rules/_find?filter=alert.attributes.params.risk_score:40` + `${getUrlPrefix( + Spaces.space1.id + )}/api/alerting/rules/_find?filter=alert.attributes.params.risk_score:40` ); expect(response.status).to.eql(200); expect(response.body.total).to.equal(1); expect(response.body.data[0].params.risk_score).to.eql(40); - if (describeType === 'public') { - expect(response.body.data[0].mapped_params).to.eql(undefined); - } + expect(response.body.data[0].mapped_params).to.eql(undefined); }); it('should error if filtering on mapped parameters directly using the public API', async () => { const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/${ - describeType === 'public' ? 'api' : 'internal' - }/alerting/rules/_find?filter=alert.attributes.mapped_params.risk_score:40` + `${getUrlPrefix( + Spaces.space1.id + )}/api/alerting/rules/_find?filter=alert.attributes.mapped_params.risk_score:40` ); - if (describeType === 'public') { - expect(response.status).to.eql(400); - expect(response.body.message).to.eql( - 'Error find rules: Filter is not supported on this field alert.attributes.mapped_params.risk_score' - ); - } else { - expect(response.status).to.eql(200); - } + expect(response.status).to.eql(400); + expect(response.body.message).to.eql( + 'Error find rules: Filter is not supported on this field alert.attributes.mapped_params.risk_score' + ); }); }); - }); -}; - -// eslint-disable-next-line import/no-default-export -export default function createFindTests({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - - describe('find', () => { - const objectRemover = new ObjectRemover(supertest); - - afterEach(() => objectRemover.removeAll()); - - findTestUtils('public', supertest, objectRemover); - findTestUtils('internal', supertest, objectRemover); describe('legacy', function () { this.tags('skipFIPS'); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/find_internal.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/find_internal.ts new file mode 100644 index 0000000000000..25fc54a5229d3 --- /dev/null +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/find_internal.ts @@ -0,0 +1,355 @@ +/* + * Copyright 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 expect from '@kbn/expect'; +import { Agent as SuperTestAgent } from 'supertest'; +import { fromKueryExpression } from '@kbn/es-query'; +import { Spaces } from '../../../scenarios'; +import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +async function createAlert( + objectRemover: ObjectRemover, + supertest: SuperTestAgent, + overwrites = {} +) { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData(overwrites)) + .expect(200); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); + return createdAlert; +} + +// eslint-disable-next-line import/no-default-export +export default function createFindTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + describe('find internal API', () => { + const objectRemover = new ObjectRemover(supertest); + + afterEach(() => objectRemover.removeAll()); + + describe('handle find alert request', function () { + this.tags('skipFIPS'); + it('should handle find alert request appropriately', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'MY action', + connector_type_id: 'test.noop', + config: {}, + secrets: {}, + }) + .expect(200); + + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + actions: [ + { + group: 'default', + id: createdAction.id, + params: {}, + frequency: { + summary: false, + notify_when: 'onThrottleInterval', + throttle: '1m', + }, + }, + ], + notify_when: undefined, + throttle: undefined, + }) + ) + .expect(200); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); + + const response = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/_find`) + .set('kbn-xsrf', 'foo') + .send({ + search: 'test.noop', + search_fields: 'alertTypeId', + }); + + expect(response.status).to.eql(200); + expect(response.body.page).to.equal(1); + expect(response.body.per_page).to.be.greaterThan(0); + expect(response.body.total).to.be.greaterThan(0); + const match = response.body.data.find((obj: any) => obj.id === createdAlert.id); + const activeSnoozes = match.active_snoozes; + const hasActiveSnoozes = !!(activeSnoozes || []).filter((obj: any) => obj).length; + expect(match).to.eql({ + id: createdAlert.id, + name: 'abc', + tags: ['foo'], + rule_type_id: 'test.noop', + revision: 0, + running: false, + consumer: 'alertsFixture', + schedule: { interval: '1m' }, + enabled: true, + actions: [ + { + group: 'default', + id: createdAction.id, + connector_type_id: 'test.noop', + params: {}, + frequency: { + summary: false, + notify_when: 'onThrottleInterval', + throttle: '1m', + }, + uuid: match.actions[0].uuid, + }, + ], + params: {}, + created_by: null, + api_key_owner: null, + api_key_created_by_user: null, + scheduled_task_id: match.scheduled_task_id, + updated_by: null, + throttle: null, + notify_when: null, + mute_all: false, + muted_alert_ids: [], + created_at: match.created_at, + updated_at: match.updated_at, + execution_status: match.execution_status, + ...(match.next_run ? { next_run: match.next_run } : {}), + ...(match.last_run ? { last_run: match.last_run } : {}), + monitoring: match.monitoring, + snooze_schedule: match.snooze_schedule, + ...(hasActiveSnoozes && { active_snoozes: activeSnoozes }), + is_snoozed_until: null, + }); + expect(Date.parse(match.created_at)).to.be.greaterThan(0); + expect(Date.parse(match.updated_at)).to.be.greaterThan(0); + }); + }); + + it(`shouldn't find alert from another space`, async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); + + await supertest + .post(`${getUrlPrefix(Spaces.other.id)}/internal/alerting/rules/_find`) + .set('kbn-xsrf', 'foo') + .send({ + search: 'test.noop', + search_fields: 'alertTypeId', + }) + .expect(200, { + page: 1, + per_page: 10, + total: 0, + data: [], + }); + }); + + describe('basic functionality', () => { + beforeEach(async () => { + await Promise.all([ + createAlert(objectRemover, supertest, { params: { strValue: 'my a' } }), + createAlert(objectRemover, supertest, { params: { strValue: 'my b' } }), + createAlert(objectRemover, supertest, { params: { strValue: 'my c' } }), + createAlert(objectRemover, supertest, { + params: { + risk_score: 60, + severity: 'high', + }, + }), + createAlert(objectRemover, supertest, { + params: { + risk_score: 40, + severity: 'medium', + }, + }), + createAlert(objectRemover, supertest, { + params: { + risk_score: 20, + severity: 'low', + }, + }), + ]); + }); + + it(`it should allow filter on monitoring attributes`, async () => { + const response = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/_find`) + .set('kbn-xsrf', 'foo') + .send({ + filter: 'alert.attributes.monitoring.run.calculated_metrics.success_ratio>50', + }); + + expect(response.status).to.eql(200); + }); + + it(`it should allow ordering on monitoring attributes`, async () => { + const response = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/_find`) + .set('kbn-xsrf', 'foo') + .send({ + sort_field: 'monitoring.run.calculated_metrics.success_ratio', + }); + + expect(response.status).to.eql(200); + }); + + it(`it should allow search_fields on monitoring attributes`, async () => { + const response = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/_find`) + .set('kbn-xsrf', 'foo') + .send({ + search_fields: 'monitoring.run.calculated_metrics.success_ratio', + search: '50', + }); + + expect(response.status).to.eql(200); + }); + + it('should filter on string parameters', async () => { + const response = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/_find`) + .set('kbn-xsrf', 'foo') + .send({ + filter: 'alert.attributes.params.strValue:"my b"', + }); + + expect(response.status).to.eql(200); + expect(response.body.total).to.equal(1); + expect(response.body.data[0].params.strValue).to.eql('my b'); + }); + + it('should filter on kueryNode parameters', async () => { + const response = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/_find`) + .set('kbn-xsrf', 'foo') + .send({ + filter: JSON.stringify(fromKueryExpression('alert.attributes.params.strValue:"my b"')), + }); + + expect(response.status).to.eql(200); + expect(response.body.total).to.equal(1); + expect(response.body.data[0].params.strValue).to.eql('my b'); + }); + + it('should sort by parameters', async () => { + const response = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/_find`) + .set('kbn-xsrf', 'foo') + .send({ + sort_field: 'params.severity', + sort_order: 'asc', + }); + expect(response.body.data[0].params.severity).to.equal('low'); + expect(response.body.data[1].params.severity).to.equal('medium'); + expect(response.body.data[2].params.severity).to.equal('high'); + }); + + it('should search by parameters', async () => { + const response = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/_find`) + .set('kbn-xsrf', 'foo') + .send({ + search_fields: 'params.severity', + search: 'medium', + }); + + expect(response.status).to.eql(200); + expect(response.body.total).to.equal(1); + expect(response.body.data[0].params.severity).to.eql('medium'); + }); + + it('should filter on parameters', async () => { + const response = await supertest + .get(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/_find`) + .set('kbn-xsrf', 'foo') + .send({ + filter: 'alert.attributes.params.risk_score:40', + }); + + expect(response.status).to.eql(200); + expect(response.body.total).to.equal(1); + expect(response.body.data[0].params.risk_score).to.eql(40); + }); + + it('should error if filtering on mapped parameters directly using the public API', async () => { + const response = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/_find`) + .set('kbn-xsrf', 'foo') + .send({ + filter: 'alert.attributes.mapped_params.risk_score:40', + }); + + expect(response.status).to.eql(200); + }); + }); + + describe('legacy', function () { + this.tags('skipFIPS'); + it('should handle find alert request appropriately', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); + + const response = await supertest.get( + `${getUrlPrefix( + Spaces.space1.id + )}/api/alerts/_find?search=test.noop&search_fields=alertTypeId` + ); + + expect(response.status).to.eql(200); + expect(response.body.page).to.equal(1); + expect(response.body.perPage).to.be.greaterThan(0); + expect(response.body.total).to.be.greaterThan(0); + const match = response.body.data.find((obj: any) => obj.id === createdAlert.id); + expect(match).to.eql({ + id: createdAlert.id, + name: 'abc', + tags: ['foo'], + alertTypeId: 'test.noop', + consumer: 'alertsFixture', + schedule: { interval: '1m' }, + enabled: true, + actions: [], + params: {}, + createdBy: null, + apiKeyOwner: null, + apiKeyCreatedByUser: null, + scheduledTaskId: match.scheduledTaskId, + updatedBy: null, + throttle: '1m', + notifyWhen: 'onThrottleInterval', + muteAll: false, + mutedInstanceIds: [], + createdAt: match.createdAt, + updatedAt: match.updatedAt, + executionStatus: match.executionStatus, + revision: 0, + running: false, + ...(match.nextRun ? { nextRun: match.nextRun } : {}), + ...(match.lastRun ? { lastRun: match.lastRun } : {}), + }); + expect(Date.parse(match.createdAt)).to.be.greaterThan(0); + expect(Date.parse(match.updatedAt)).to.be.greaterThan(0); + }); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/update.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/update.ts index 029775fbba383..025fa3b693dce 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/update.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group2/update.ts @@ -91,11 +91,10 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { expect(Date.parse(response.body.next_run)).to.be.greaterThan(0); } - response = await supertest.get( - `${getUrlPrefix( - Spaces.space1.id - )}/internal/alerting/rules/_find?filter=alert.attributes.params.risk_score:40` - ); + response = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/_find`) + .set('kbn-xsrf', 'kibana') + .send({ filter: `alert.attributes.params.risk_score:40` }); expect(response.body.data[0].mapped_params).to.eql({ risk_score: 40, diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data/alerts_as_data_flapping.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data/alerts_as_data_flapping.ts index 4ee2ea9e18c3c..1035ba1902dfe 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data/alerts_as_data_flapping.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/alerts_as_data/alerts_as_data_flapping.ts @@ -35,9 +35,7 @@ export default function createAlertsAsDataFlappingTest({ getService }: FtrProvid const alertsAsDataIndex = '.alerts-test.patternfiring.alerts-default'; - // FLAKY: https://github.com/elastic/kibana/issues/195573 - // Failing: See https://github.com/elastic/kibana/issues/195573 - describe.skip('alerts as data flapping', function () { + describe('alerts as data flapping', function () { this.tags('skipFIPS'); beforeEach(async () => { await es.deleteByQuery({ @@ -712,6 +710,9 @@ export default function createAlertsAsDataFlappingTest({ getService }: FtrProvid }) .expect(200); + // wait so cache expires + await setTimeoutAsync(TEST_CACHE_EXPIRATION_TIME); + // Wait for the rule to run once let run = 1; let runWhichItFlapped = 0; @@ -754,6 +755,11 @@ export default function createAlertsAsDataFlappingTest({ getService }: FtrProvid const searchResult = await es.search({ index: alertsAsDataIndex, body: { + sort: [ + { + '@timestamp': 'desc', + }, + ], query: { bool: { must: { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/builtin_alert_types/long_running/rule.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/builtin_alert_types/long_running/rule.ts index 7ea429f1be92f..49ad3abeb063b 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/builtin_alert_types/long_running/rule.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/builtin_alert_types/long_running/rule.ts @@ -110,11 +110,13 @@ export default function ruleTests({ getService }: FtrProviderContext) { }); }); - const { status, body: rule } = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${ruleId}` - ); - expect(status).to.eql(200); - expect(rule.execution_status.status).to.eql('active'); + await retry.try(async () => { + const { status, body: rule } = await supertest.get( + `${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${ruleId}` + ); + expect(status).to.eql(200); + expect(rule.execution_status.status).to.eql('active'); + }); }); it('still logs alert docs when rule exceeds timeout when cancelAlertsOnRuleTimeout is false on rule type', async () => { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/notify_when.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/notify_when.ts index e32813934e4c2..5d7b6fc29264b 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/notify_when.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/notify_when.ts @@ -92,7 +92,11 @@ export default function createNotifyWhenTests({ getService }: FtrProviderContext }); }); - const executeActionEvents = getEventsByAction(events, 'execute-action'); + // Slice in case the rule ran more times than we are asserting on + const executeActionEvents = getEventsByAction(events, 'execute-action').slice( + 0, + expectedActionGroupBasedOnPattern.length + ); const executeActionEventsActionGroup = executeActionEvents.map( (event) => event?.kibana?.alerting?.action_group_id ); diff --git a/x-pack/test/api_integration/apis/ml/anomaly_detectors/forecast_with_spaces.ts b/x-pack/test/api_integration/apis/ml/anomaly_detectors/forecast_with_spaces.ts index 32e82c67e348d..a6bc0b0c99a86 100644 --- a/x-pack/test/api_integration/apis/ml/anomaly_detectors/forecast_with_spaces.ts +++ b/x-pack/test/api_integration/apis/ml/anomaly_detectors/forecast_with_spaces.ts @@ -15,6 +15,7 @@ export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertestWithoutAuth'); const ml = getService('ml'); const spacesService = getService('spaces'); + const retry = getService('retry'); const forecastJobId = 'fq_single_forecast'; const forecastJobDatafeedId = `datafeed-${forecastJobId}`; @@ -45,21 +46,22 @@ export default ({ getService }: FtrProviderContext) => { user: USER, expectedStatusCode: number ) { - const { body, status } = await supertest - .delete( - `${ - space ? `/s/${space}` : '' - }/internal/ml/anomaly_detectors/${jobId}/_forecast/${forecastId}` - ) - .auth(user, ml.securityCommon.getPasswordForUser(user)) - .set(getCommonRequestHeader('1')); - ml.api.assertResponseStatusCode(expectedStatusCode, status, body); - - return body; + await retry.tryForTime(10000, async () => { + const { body, status } = await supertest + .delete( + `${ + space ? `/s/${space}` : '' + }/internal/ml/anomaly_detectors/${jobId}/_forecast/${forecastId}` + ) + .auth(user, ml.securityCommon.getPasswordForUser(user)) + .set(getCommonRequestHeader('1')); + ml.api.assertResponseStatusCode(expectedStatusCode, status, body); + + return body; + }); } - // Failing see: https://github.com/elastic/kibana/issues/195602 - describe.skip('POST anomaly_detectors _forecast with spaces', function () { + describe('POST anomaly_detectors _forecast with spaces', function () { let forecastId: string; before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); @@ -110,11 +112,11 @@ export default ({ getService }: FtrProviderContext) => { }); it('should not delete forecast for user without permissions', async () => { - await await deleteForecast(forecastJobId, forecastId, idSpace1, USER.ML_VIEWER, 403); + await deleteForecast(forecastJobId, forecastId, idSpace1, USER.ML_VIEWER, 403); }); it('should delete forecast for user with permissions', async () => { - await await deleteForecast(forecastJobId, forecastId, idSpace1, USER.ML_POWERUSER, 200); + await deleteForecast(forecastJobId, forecastId, idSpace1, USER.ML_POWERUSER, 200); }); it('should not run forecast for open job with invalid duration', async () => { diff --git a/x-pack/test/api_integration/apis/ml/system/has_privileges.ts b/x-pack/test/api_integration/apis/ml/system/has_privileges.ts new file mode 100644 index 0000000000000..2e705240b403e --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/system/has_privileges.ts @@ -0,0 +1,143 @@ +/* + * Copyright 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 expect from '@kbn/expect'; + +import { MlHasPrivilegesResponse } from '@kbn/ml-plugin/public/application/services/ml_api_service'; +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { getCommonRequestHeader } from '../../../../functional/services/ml/common_api'; +import { USER } from '../../../../functional/services/ml/security_common'; + +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertestWithoutAuth'); + const ml = getService('ml'); + + async function runRequest( + user: USER, + index: any, + expectedStatusCode = 200 + ): Promise { + const { body, status } = await supertest + .post(`/internal/ml/_has_privileges`) + .auth(user, ml.securityCommon.getPasswordForUser(user)) + .set(getCommonRequestHeader('1')) + .send({ index }); + ml.api.assertResponseStatusCode(expectedStatusCode, status, body); + + return body; + } + + const testData = [ + { + user: USER.ML_POWERUSER, + index: [ + { + names: ['ft_farequote_small'], + privileges: ['read'], + }, + { + names: ['ft_farequote_small'], + privileges: ['write'], + }, + ], + expectedResponse: { + hasPrivileges: { + username: 'ft_ml_poweruser', + has_all_requested: false, + cluster: {}, + index: { + ft_farequote_small: { + read: true, + write: false, + }, + }, + application: {}, + }, + upgradeInProgress: false, + }, + expectedStatusCode: 200, + }, + { + user: USER.ML_VIEWER, + index: [ + { + names: ['ft_farequote_small'], + privileges: ['read'], + }, + { + names: ['ft_farequote_small'], + privileges: ['write'], + }, + ], + expectedResponse: { + hasPrivileges: { + username: 'ft_ml_viewer', + has_all_requested: false, + cluster: {}, + index: { + ft_farequote_small: { + read: true, + write: false, + }, + }, + application: {}, + }, + upgradeInProgress: false, + }, + + expectedStatusCode: 200, + }, + { + user: USER.ML_UNAUTHORIZED, + index: [ + { + names: ['ft_farequote_small'], + privileges: ['read'], + }, + { + names: ['ft_farequote_small'], + privileges: ['write'], + }, + ], + expectedResponse: { statusCode: 403, error: 'Forbidden', message: 'Forbidden' }, + expectedStatusCode: 403, + }, + ]; + + describe("ML's _has_privileges", () => { + before(async () => { + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote_small'); + }); + after(async () => { + await ml.api.setUpgradeMode(false); + }); + + it('should return correct privileges for test data', async () => { + for (const { user, index, expectedResponse, expectedStatusCode } of testData) { + const response = await runRequest(user, index, expectedStatusCode); + expect(response).to.eql( + expectedResponse, + `expected ${JSON.stringify(expectedResponse)}, got ${JSON.stringify(response)}` + ); + } + }); + + it('should return correct upgrade in progress', async () => { + const index = testData[0].index; + const expectedResponse = { ...testData[0].expectedResponse, upgradeInProgress: true }; + await ml.api.setUpgradeMode(true); + await ml.api.assertUpgradeMode(true); + + const response = await runRequest(USER.ML_POWERUSER, index); + expect(response).to.eql( + expectedResponse, + `expected ${JSON.stringify(expectedResponse)}, got ${JSON.stringify(response)}` + ); + }); + }); +}; diff --git a/x-pack/test/api_integration/apis/ml/system/index.ts b/x-pack/test/api_integration/apis/ml/system/index.ts index 0ddd4b80a0ee5..42e02b5fbb808 100644 --- a/x-pack/test/api_integration/apis/ml/system/index.ts +++ b/x-pack/test/api_integration/apis/ml/system/index.ts @@ -14,5 +14,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./index_exists')); loadTestFile(require.resolve('./info')); loadTestFile(require.resolve('./node_count')); + loadTestFile(require.resolve('./has_privileges')); }); } diff --git a/x-pack/test/api_integration/apis/slos/get_slo.ts b/x-pack/test/api_integration/apis/slos/get_slo.ts index 274c2535a4630..815409853c7d6 100644 --- a/x-pack/test/api_integration/apis/slos/get_slo.ts +++ b/x-pack/test/api_integration/apis/slos/get_slo.ts @@ -13,9 +13,24 @@ import { loadTestData } from './helper/load_test_data'; import { SloEsClient } from './helper/es'; import { sloData } from './fixtures/create_slo'; +export const expectSummary = (summary: Record) => { + expect(summary).toEqual({ + sliValue: expect.any(Number), + errorBudget: { + initial: expect.any(Number), + consumed: expect.any(Number), + remaining: expect.any(Number), + isEstimated: expect.any(Boolean), + }, + status: expect.any(String), + fiveMinuteBurnRate: expect.any(Number), + oneDayBurnRate: expect.any(Number), + oneHourBurnRate: expect.any(Number), + }); +}; + export default function ({ getService }: FtrProviderContext) { - // FLAKY: https://github.com/elastic/kibana/issues/177806 - describe.skip('Get SLOs', function () { + describe('GetSLOs', function () { this.tags('skipCloud'); const supertestAPI = getService('supertest'); @@ -23,8 +38,16 @@ export default function ({ getService }: FtrProviderContext) { const logger = getService('log'); const retry = getService('retry'); const slo = getService('slo'); + // const transform = getService('transform'); const sloEsClient = new SloEsClient(esClient); + // const onFailure = async () => { + // const allTransforms = await transform.api.getTransformList(); + // for (const tf of allTransforms.transforms) { + // await transform.api.scheduleTransform(tf.id); + // } + // }; + let createSLOInput: CreateSLOInput; const createSLO = async (requestOverrides?: Record) => { @@ -97,24 +120,13 @@ export default function ({ getService }: FtrProviderContext) { version: 2, instanceId: '*', meta: {}, - summary: { - sliValue: 0.5, - errorBudget: { - initial: 0.01, - consumed: 50, - remaining: -49, - isEstimated: false, - }, - fiveMinuteBurnRate: 40, - oneDayBurnRate: 50, - oneHourBurnRate: 50, - status: 'VIOLATED', - }, + summary: expect.any(Object), }); + expectSummary(getResponse.body.summary); }); }); - it('gets slo by id and calculates SLI - occurences calendarAligned', async () => { + it('gets slo by id and calculates SLI - occurrences calendarAligned', async () => { const response = await createSLO({ groupBy: '*', timeWindow: { @@ -160,20 +172,9 @@ export default function ({ getService }: FtrProviderContext) { version: 2, instanceId: '*', meta: {}, - summary: { - sliValue: 0.5, - errorBudget: { - initial: 0.01, - consumed: 50, - remaining: -49, - isEstimated: true, - }, - fiveMinuteBurnRate: 40, - oneDayBurnRate: 50, - oneHourBurnRate: 50, - status: 'VIOLATED', - }, + summary: expect.any(Object), }); + expectSummary(getResponse.body.summary); }); }); @@ -233,17 +234,9 @@ export default function ({ getService }: FtrProviderContext) { version: 2, instanceId: '*', meta: {}, - summary: expect.objectContaining({ - sliValue: 0.5, - errorBudget: { - initial: 0.01, - consumed: 50, - remaining: -49, - isEstimated: false, - }, - status: 'VIOLATED', - }), + summary: expect.any(Object), }); + expectSummary(getResponse.body.summary); }); }); @@ -302,20 +295,9 @@ export default function ({ getService }: FtrProviderContext) { version: 2, instanceId: '*', meta: {}, - summary: { - sliValue: 0, - errorBudget: { - initial: 0.01, - consumed: 0.198413, - remaining: 0.801587, - isEstimated: false, - }, - fiveMinuteBurnRate: 40, - oneDayBurnRate: 50, - oneHourBurnRate: 50, - status: 'DEGRADING', - }, + summary: expect.any(Object), }); + expectSummary(getResponse.body.summary); }); }); @@ -366,34 +348,39 @@ export default function ({ getService }: FtrProviderContext) { }); }); - it('gets slos instances', async () => { - const createResponse = await createSLO(); - const id = createResponse.body.id; - - await retry.tryForTime(400 * 1000, async () => { - const response = await supertestAPI - .get(`/api/observability/slos`) - .set('kbn-xsrf', 'true') - .send() - .expect(200); - - expect(response.body.results.length).toEqual(3); - - response.body.results.forEach((result: Record, i: number) => { - expect(result.groupings).toEqual(expect.objectContaining({ tags: `${i + 1}` })); - }); - - const instanceResponse = await supertestAPI - .get(`/internal/observability/slos/${id}/_instances`) - .set('kbn-xsrf', 'true') - .send() - .expect(200); - - // expect 3 instances to be created - expect(instanceResponse.body.groupBy).toEqual('tags'); - expect(instanceResponse.body.instances.sort()).toEqual(['tags:1', 'tags:2', 'tags:3']); - }); - }); + // not possible for now to reliably fix this + // it.skip('gets slos instances', async () => { + // const createResponse = await createSLO(); + // const id = createResponse.body.id; + // + // await retry.tryForTime( + // 400 * 1000, + // async () => { + // const response = await supertestAPI + // .get(`/api/observability/slos`) + // .set('kbn-xsrf', 'true') + // .send() + // .expect(200); + // const res = response.body.results; + // expect(res.length).toEqual(3); + // const groups = res.map((r: any) => r.groupings.tags); + // + // expect(groups.sort()).toEqual(['1', '2', '3']); + // + // const instanceResponse = await supertestAPI + // .get(`/internal/observability/slos/${id}/_instances`) + // .set('kbn-xsrf', 'true') + // .send() + // .expect(200); + // + // // expect 3 instances to be created + // expect(instanceResponse.body.groupBy).toEqual('tags'); + // expect(instanceResponse.body.instances.sort()).toEqual(['1', '2', '3']); + // }, + // onFailure, + // 10 * 1000 + // ); + // }); it('gets slo definitions', async () => { const createResponse = await createSLO(); diff --git a/x-pack/test/api_integration/apis/slos/index.ts b/x-pack/test/api_integration/apis/slos/index.ts index c80a0c58e5ecc..3401b195ccee5 100644 --- a/x-pack/test/api_integration/apis/slos/index.ts +++ b/x-pack/test/api_integration/apis/slos/index.ts @@ -9,9 +9,9 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('SLO API Tests', () => { + loadTestFile(require.resolve('./get_slo')); loadTestFile(require.resolve('./create_slo')); loadTestFile(require.resolve('./delete_slo')); - loadTestFile(require.resolve('./get_slo')); loadTestFile(require.resolve('./update_slo')); loadTestFile(require.resolve('./reset_slo')); loadTestFile(require.resolve('./fetch_historical_summary')); diff --git a/x-pack/test/api_integration/deployment_agnostic/default_configs/serverless.config.base.ts b/x-pack/test/api_integration/deployment_agnostic/default_configs/serverless.config.base.ts index f73af3a6d4bf7..e7df37f5aa312 100644 --- a/x-pack/test/api_integration/deployment_agnostic/default_configs/serverless.config.base.ts +++ b/x-pack/test/api_integration/deployment_agnostic/default_configs/serverless.config.base.ts @@ -113,10 +113,6 @@ export function createServerlessTestConfig => { + const res = await supertest + .post(`/api/fleet/outputs`) + .set('kbn-xsrf', 'xxxx') + .send({ + id, + name, + type, + hosts, + }) + .expect(200); + return res.body.item.id; + }; + + const createAgentPolicy = async ( + name: string, + id: string, + dataOutputId?: string, + monitoringOutputId?: string + ): Promise => { + const res = await supertest + .post(`/api/fleet/agent_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + name, + id, + namespace: 'default', + ...(dataOutputId ? { data_output_id: dataOutputId } : {}), + ...(monitoringOutputId ? { monitoring_output_id: monitoringOutputId } : {}), + }) + .expect(200); + return res.body.item; + }; + + const createAgentPolicyWithPackagePolicy = async ({ + name, + id, + outputId, + }: { + name: string; + id: string; + outputId?: string; + }): Promise => { + const { body: res } = await supertest + .post(`/api/fleet/agent_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + name, + namespace: 'default', + id, + }) + .expect(200); + + const agentPolicyWithPPId = res.item.id; + // package policy needs to have a custom output_id + await supertest + .post(`/api/fleet/package_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'filetest-1', + description: '', + namespace: 'default', + ...(outputId ? { output_id: outputId } : {}), + policy_id: agentPolicyWithPPId, + inputs: [], + package: { + name: 'filetest', + title: 'For File Tests', + version: '0.1.0', + }, + }) + .expect(200); + return res.item; + }; + + let output1Id = ''; + describe('fleet_agent_policies_outputs', () => { + describe('POST /api/fleet/agent_policies/outputs', () => { + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/fleet/empty_fleet_server'); + await kibanaServer.savedObjects.cleanStandardList(); + await fleetAndAgents.setup(); + + output1Id = await createOutput({ + name: 'Output 1', + id: 'logstash-output-1', + type: 'logstash', + hosts: ['test.fr:443'], + }); + }); + after(async () => { + await supertest + .delete(`/api/fleet/outputs/${output1Id}`) + .set('kbn-xsrf', 'xxxx') + .expect(200); + }); + + it('should get a list of outputs by agent policies', async () => { + await createAgentPolicy('Agent policy with default output', 'agent-policy-1'); + await createAgentPolicy( + 'Agent policy with custom output', + 'agent-policy-2', + output1Id, + output1Id + ); + + const outputsPerPoliciesRes = await supertest + .post(`/api/fleet/agent_policies/outputs`) + .set('kbn-xsrf', 'xxxx') + .send({ + ids: ['agent-policy-1', 'agent-policy-2'], + }) + .expect(200); + expect(outputsPerPoliciesRes.body.items).to.eql([ + { + agentPolicyId: 'agent-policy-1', + monitoring: { + output: { + name: 'default', + id: 'fleet-default-output', + }, + }, + data: { + output: { + name: 'default', + id: 'fleet-default-output', + }, + integrations: [], + }, + }, + { + agentPolicyId: 'agent-policy-2', + monitoring: { + output: { + name: 'Output 1', + id: 'logstash-output-1', + }, + }, + data: { + output: { + name: 'Output 1', + id: 'logstash-output-1', + }, + integrations: [], + }, + }, + ]); + // clean up policies + await supertest + .post(`/api/fleet/agent_policies/delete`) + .send({ agentPolicyId: 'agent-policy-1' }) + .set('kbn-xsrf', 'xxxx') + .expect(200); + await supertest + .post(`/api/fleet/agent_policies/delete`) + .send({ agentPolicyId: 'agent-policy-2' }) + .set('kbn-xsrf', 'xxxx') + .expect(200); + }); + }); + + let output2Id = ''; + describe('GET /api/fleet/agent_policies/{agentPolicyId}/outputs', () => { + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/fleet/empty_fleet_server'); + await kibanaServer.savedObjects.cleanStandardList(); + await fleetAndAgents.setup(); + + output2Id = await createOutput({ + name: 'ES Output 1', + id: 'es-output-1', + type: 'elasticsearch', + hosts: ['https://test.fr:8080'], + }); + }); + after(async () => { + await supertest + .delete(`/api/fleet/outputs/${output2Id}`) + .set('kbn-xsrf', 'xxxx') + .expect(200); + }); + + it('should get the list of outputs related to an agentPolicy id', async () => { + await createAgentPolicy('Agent policy with ES output', 'agent-policy-custom', output2Id); + + const outputsPerPoliciesRes = await supertest + .get(`/api/fleet/agent_policies/agent-policy-custom/outputs`) + .set('kbn-xsrf', 'xxxx') + .expect(200); + expect(outputsPerPoliciesRes.body.item).to.eql({ + monitoring: { + output: { + name: 'default', + id: 'fleet-default-output', + }, + }, + data: { + output: { + name: 'ES Output 1', + id: 'es-output-1', + }, + integrations: [], + }, + }); + + await supertest + .post(`/api/fleet/agent_policies/delete`) + .send({ agentPolicyId: 'agent-policy-custom' }) + .set('kbn-xsrf', 'xxxx') + .expect(200); + }); + + it('should also list the outputs set on integrations if any', async () => { + await createAgentPolicyWithPackagePolicy({ + name: 'Agent Policy with package policy', + id: 'agent-policy-custom-2', + outputId: output2Id, + }); + + const outputsPerPoliciesRes = await supertest + .get(`/api/fleet/agent_policies/agent-policy-custom-2/outputs`) + .set('kbn-xsrf', 'xxxx') + .expect(200); + expect(outputsPerPoliciesRes.body.item).to.eql({ + monitoring: { + output: { + name: 'default', + id: 'fleet-default-output', + }, + }, + data: { + output: { + name: 'default', + id: 'fleet-default-output', + }, + integrations: [ + { + id: 'es-output-1', + integrationPolicyName: 'filetest-1', + name: 'ES Output 1', + }, + ], + }, + }); + + await supertest + .post(`/api/fleet/agent_policies/delete`) + .send({ agentPolicyId: 'agent-policy-custom-2' }) + .set('kbn-xsrf', 'xxxx') + .expect(200); + }); + }); + }); +} diff --git a/x-pack/test/fleet_api_integration/apis/agent_policy/index.js b/x-pack/test/fleet_api_integration/apis/agent_policy/index.js index 9ae58b0089942..b036ab9d8103d 100644 --- a/x-pack/test/fleet_api_integration/apis/agent_policy/index.js +++ b/x-pack/test/fleet_api_integration/apis/agent_policy/index.js @@ -13,5 +13,6 @@ export default function loadTests({ loadTestFile }) { loadTestFile(require.resolve('./privileges')); loadTestFile(require.resolve('./agent_policy_root_integrations')); loadTestFile(require.resolve('./create_standalone_api_key')); + loadTestFile(require.resolve('./agent_policy_outputs')); }); } diff --git a/x-pack/test/fleet_api_integration/apis/integrations/inputs_with_standalone_docker_agent.ts b/x-pack/test/fleet_api_integration/apis/integrations/inputs_with_standalone_docker_agent.ts index 0f751711904ae..b5376142ab854 100644 --- a/x-pack/test/fleet_api_integration/apis/integrations/inputs_with_standalone_docker_agent.ts +++ b/x-pack/test/fleet_api_integration/apis/integrations/inputs_with_standalone_docker_agent.ts @@ -25,7 +25,8 @@ export default function (providerContext: FtrProviderContext) { const config = getService('config'); const log = getService('log'); - describe('inputs_with_standalone_docker_agent', () => { + // Failing: See https://github.com/elastic/kibana/issues/193625 + describe.skip('inputs_with_standalone_docker_agent', () => { skipIfNoDockerRegistry(providerContext); let apiKey: string; let agent: AgentProcess; diff --git a/x-pack/test/functional/apps/lens/group1/multiple_data_views.ts b/x-pack/test/functional/apps/lens/group1/multiple_data_views.ts index 84bf66c26191d..d65b10d0056e1 100644 --- a/x-pack/test/functional/apps/lens/group1/multiple_data_views.ts +++ b/x-pack/test/functional/apps/lens/group1/multiple_data_views.ts @@ -33,16 +33,26 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { function assertMatchesExpectedData( state: DebugState, - expectedData: Array> + expectedData: Array>, + chartType: 'bars' | 'lines' = 'bars' ) { - expect( - state?.bars?.map(({ bars }) => - bars.map((bar) => ({ - x: bar.x, - y: Math.floor(bar.y * 100) / 100, - })) - ) - ).to.eql(expectedData); + if (chartType === 'lines') { + expect( + state?.lines + ?.map(({ points }) => + points + .map((point) => ({ x: point.x, y: Math.floor(point.y * 100) / 100 })) + .sort(({ x }, { x: x2 }) => x - x2) + ) + .filter((a) => a.length > 0) + ).to.eql(expectedData); + } else { + expect( + state?.bars?.map(({ bars }) => + bars.map((point) => ({ x: point.x, y: Math.floor(point.y * 100) / 100 })) + ) + ).to.eql(expectedData); + } } describe('lens with multiple data views', () => { @@ -93,13 +103,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.click('fieldToggle-DistanceKilometers'); const data = await lens.getCurrentChartDebugState('xyVisChart'); - assertMatchesExpectedData(data, [expectedLogstashData, expectedFlightsData]); + assertMatchesExpectedData(data, [expectedLogstashData, expectedFlightsData], 'lines'); }); it('ignores global filters on layers using a data view without the filter field', async () => { await filterBar.addFilter({ field: 'Carrier', operation: 'exists' }); const data = await lens.getCurrentChartDebugState('xyVisChart'); - assertMatchesExpectedData(data, [expectedLogstashData, expectedFlightsData]); + assertMatchesExpectedData(data, [expectedLogstashData, expectedFlightsData], 'lines'); await lens.save(visTitle); }); @@ -110,7 +120,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await visualize.openSavedVisualization(visTitle); const data = await lens.getCurrentChartDebugState('xyVisChart'); - assertMatchesExpectedData(data, [expectedFlightsData]); + assertMatchesExpectedData(data, [expectedFlightsData], 'lines'); }); }); } diff --git a/x-pack/test/functional/apps/spaces/solution_view_flag_enabled/solution_tour.ts b/x-pack/test/functional/apps/spaces/solution_view_flag_enabled/solution_tour.ts index 852a2a83031cd..7b058aa36ba92 100644 --- a/x-pack/test/functional/apps/spaces/solution_view_flag_enabled/solution_tour.ts +++ b/x-pack/test/functional/apps/spaces/solution_view_flag_enabled/solution_tour.ts @@ -29,13 +29,15 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await es .delete( - { id: `config-global:${version}`, index: '.kibana', refresh: true }, + { id: `config-global:${version}`, index: '.kibana', refresh: 'wait_for' }, { headers: { 'kbn-xsrf': 'spaces' } } ) .catch((error) => { if (error.statusCode === 404) return; // ignore 404 errors throw error; }); + + await PageObjects.common.sleep(500); // just to be on the safe side }; before(async () => { diff --git a/x-pack/test/functional/page_objects/api_keys_page.ts b/x-pack/test/functional/page_objects/api_keys_page.ts index 8f74f927b976f..9b196f70eeef0 100644 --- a/x-pack/test/functional/page_objects/api_keys_page.ts +++ b/x-pack/test/functional/page_objects/api_keys_page.ts @@ -78,15 +78,25 @@ export function ApiKeysPageProvider({ getService }: FtrProviderContext) { return euiCallOutHeader.getVisibleText(); }, + async isPromptPage() { + return await testSubjects.exists('apiKeysCreatePromptButton'); + }, + async getApiKeysFirstPromptTitle() { const titlePromptElem = await find.byCssSelector('.euiEmptyPrompt .euiTitle'); return await titlePromptElem.getVisibleText(); }, + async deleteApiKeyByName(apiKeyName: string) { + await testSubjects.click(`apiKeysTableDeleteAction-${apiKeyName}`); + await testSubjects.click('confirmModalConfirmButton'); + await testSubjects.waitForDeleted(`apiKeyRowName-${apiKeyName}`); + }, + async deleteAllApiKeyOneByOne() { - const hasApiKeysToDelete = await testSubjects.exists('apiKeysTableDeleteAction'); + const hasApiKeysToDelete = await testSubjects.exists('*apiKeysTableDeleteAction'); if (hasApiKeysToDelete) { - const apiKeysToDelete = await testSubjects.findAll('apiKeysTableDeleteAction'); + const apiKeysToDelete = await testSubjects.findAll('*apiKeysTableDeleteAction'); for (const element of apiKeysToDelete) { await element.click(); await testSubjects.click('confirmModalConfirmButton'); @@ -113,6 +123,10 @@ export function ApiKeysPageProvider({ getService }: FtrProviderContext) { await testSubjects.existOrFail(`apiKeyRowName-${apiKeyName}`); }, + async doesApiKeyExist(apiKeyName: string) { + return await testSubjects.exists(`apiKeyRowName-${apiKeyName}`); + }, + async getMetadataSwitch() { return await testSubjects.find('apiKeysMetadataSwitch'); }, diff --git a/x-pack/test/functional/services/ml/api.ts b/x-pack/test/functional/services/ml/api.ts index 8b2c46d474c0c..0a3d988fd750c 100644 --- a/x-pack/test/functional/services/ml/api.ts +++ b/x-pack/test/functional/services/ml/api.ts @@ -1696,5 +1696,33 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { this.assertResponseStatusCode(200, status, module); return module; }, + + async setUpgradeMode(enabled: boolean) { + log.debug(`Setting upgrade mode to "${enabled}"`); + const { body, status } = await esSupertest.post(`/_ml/set_upgrade_mode?enabled=${enabled}`); + this.assertResponseStatusCode(200, status, body); + + log.debug(`Upgrade mode set to "${enabled}"`); + }, + + async assertUpgradeMode(expectedMode: boolean) { + log.debug(`Asserting upgrade mode is "${expectedMode}"`); + const { body, status } = await esSupertest.get('/_ml/info'); + this.assertResponseStatusCode(200, status, body); + + expect(body.upgrade_mode).to.eql( + expectedMode, + `Expected upgrade mode to be ${expectedMode}, got ${body.upgrade_mode}` + ); + }, + + async getMlInfo() { + log.debug(`Getting ML info`); + const { body, status } = await kbnSupertest + .get(`/internal/ml/info`) + .set(getCommonRequestHeader('1')); + this.assertResponseStatusCode(200, status, body); + return body; + }, }; } diff --git a/x-pack/test/functional/services/transform/api.ts b/x-pack/test/functional/services/transform/api.ts index 89eb65df40ec0..66047a651a891 100644 --- a/x-pack/test/functional/services/transform/api.ts +++ b/x-pack/test/functional/services/transform/api.ts @@ -285,6 +285,15 @@ export function TransformAPIProvider({ getService }: FtrProviderContext) { } }, + async scheduleTransform(transformId: string, assertSuccess = true) { + log.debug(`Scheduling now transform '${transformId}' ...`); + const { body, status } = await esSupertest.post(`/_transform/${transformId}/_schedule_now`); + + if (assertSuccess) { + this.assertResponseStatusCode(200, status, body); + } + }, + async stopTransform(transformId: string) { log.debug(`Stopping transform '${transformId}' ...`); const { body, status } = await esSupertest.post(`/_transform/${transformId}/_stop`); diff --git a/x-pack/test/observability_functional/apps/observability/pages/alerts/custom_threshold.ts b/x-pack/test/observability_functional/apps/observability/pages/alerts/custom_threshold.ts index 38d308a17e7b0..91cb7eec19ef6 100644 --- a/x-pack/test/observability_functional/apps/observability/pages/alerts/custom_threshold.ts +++ b/x-pack/test/observability_functional/apps/observability/pages/alerts/custom_threshold.ts @@ -209,7 +209,10 @@ export default ({ getService }: FtrProviderContext) => { }); it('saved the rule correctly', async () => { - const { body: rules } = await supertest.get('/internal/alerting/rules/_find'); + const { body: rules } = await supertest + .post('/internal/alerting/rules/_find') + .set('kbn-xsrf', 'kibana') + .send({}); expect(rules.data.length).toEqual(1); expect(rules.data[0]).toEqual( diff --git a/x-pack/test/observability_functional/apps/observability/pages/rules_page.ts b/x-pack/test/observability_functional/apps/observability/pages/rules_page.ts index abc2534efaf8a..4ec0f412e1f03 100644 --- a/x-pack/test/observability_functional/apps/observability/pages/rules_page.ts +++ b/x-pack/test/observability_functional/apps/observability/pages/rules_page.ts @@ -29,7 +29,12 @@ export default ({ getService, getPageObjects }: FtrProviderContext) => { const { body: { data: rules }, } = await supertest - .get(`${INTERNAL_RULE_ENDPOINT}/_find?search=${name}&search_fields=name`) + .post(`${INTERNAL_RULE_ENDPOINT}/_find`) + .set('kbn-xsrf', 'kibana') + .send({ + search: name, + search_fields: ['name'], + }) .expect(200); return rules.find((rule: any) => rule.name === name); } diff --git a/x-pack/test/osquery_cypress/artifact_manager.ts b/x-pack/test/osquery_cypress/artifact_manager.ts index 54b9a70d37aff..a78a37267ec5a 100644 --- a/x-pack/test/osquery_cypress/artifact_manager.ts +++ b/x-pack/test/osquery_cypress/artifact_manager.ts @@ -6,5 +6,5 @@ */ export async function getLatestVersion(): Promise { - return '8.11.0-SNAPSHOT'; + return '8.15.0-SNAPSHOT'; } diff --git a/x-pack/test/osquery_cypress/cli_config.ts b/x-pack/test/osquery_cypress/cli_config.ts index a61951e405960..711bbe266db9f 100644 --- a/x-pack/test/osquery_cypress/cli_config.ts +++ b/x-pack/test/osquery_cypress/cli_config.ts @@ -6,14 +6,14 @@ */ import { FtrConfigProviderContext } from '@kbn/test'; - +import { services } from './services'; import { startOsqueryCypress } from './runner'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { const osqueryCypressConfig = await readConfigFile(require.resolve('./config.ts')); return { ...osqueryCypressConfig.getAll(), - + services, testRunner: startOsqueryCypress, }; } diff --git a/x-pack/test/osquery_cypress/services.ts b/x-pack/test/osquery_cypress/services.ts index 95fd493e6f668..272cf7eb8da4e 100644 --- a/x-pack/test/osquery_cypress/services.ts +++ b/x-pack/test/osquery_cypress/services.ts @@ -5,10 +5,4 @@ * 2.0. */ -import { commonFunctionalServices } from '@kbn/ftr-common-functional-services'; -import { commonFunctionalUIServices } from '@kbn/ftr-common-functional-ui-services'; - -export const services = { - ...commonFunctionalServices, - ...commonFunctionalUIServices, -} as const; +export * from '@kbn/test-suites-src/common/services'; diff --git a/x-pack/test/plugin_api_integration/test_suites/task_manager/metrics_route.ts b/x-pack/test/plugin_api_integration/test_suites/task_manager/metrics_route.ts index fb8ee402fcc88..50568fe1c206c 100644 --- a/x-pack/test/plugin_api_integration/test_suites/task_manager/metrics_route.ts +++ b/x-pack/test/plugin_api_integration/test_suites/task_manager/metrics_route.ts @@ -49,10 +49,18 @@ export default function ({ getService }: FtrProviderContext) { describe('task manager metrics', () => { describe('task claim', () => { it('should increment task claim success/total counters', async () => { - // counters are reset every 30 seconds, so wait until the start of a - // fresh counter cycle to make sure values are incrementing + // reset metrics counter + await getMetrics(true); + const metricsResetTime = Date.now(); + // we've resetted the metrics and have 30 seconds before they reset again + // wait for the first set of metrics to be returned after the reset const initialMetrics = ( - await getMetrics(false, (metrics) => metrics?.metrics?.task_claim?.value.total === 1) + await getMetrics( + false, + (metrics) => + !!metrics?.metrics?.task_claim?.timestamp && + new Date(metrics?.metrics?.task_claim?.timestamp).getTime() > metricsResetTime + ) ).metrics; expect(initialMetrics).not.to.be(null); expect(initialMetrics?.task_claim).not.to.be(null); @@ -92,7 +100,7 @@ export default function ({ getService }: FtrProviderContext) { const initialMetrics = ( await getMetrics( false, - (metrics) => metrics?.metrics?.task_claim?.value.total === initialCounterValue + (metrics) => (metrics?.metrics?.task_claim?.value.total || 0) >= initialCounterValue ) ).metrics; expect(initialMetrics).not.to.be(null); @@ -101,7 +109,10 @@ export default function ({ getService }: FtrProviderContext) { // retry until counter value resets const resetMetrics = ( - await getMetrics(false, (m: NodeMetrics) => m?.metrics?.task_claim?.value.total === 1) + await getMetrics( + false, + (m: NodeMetrics) => (m?.metrics?.task_claim?.value.total || 0) >= 1 + ) ).metrics; expect(resetMetrics).not.to.be(null); expect(resetMetrics?.task_claim).not.to.be(null); @@ -113,7 +124,7 @@ export default function ({ getService }: FtrProviderContext) { const initialMetrics = ( await getMetrics( false, - (metrics) => metrics?.metrics?.task_claim?.value.total === initialCounterValue + (metrics) => (metrics?.metrics?.task_claim?.value.total || 0) >= initialCounterValue ) ).metrics; expect(initialMetrics).not.to.be(null); @@ -133,8 +144,8 @@ export default function ({ getService }: FtrProviderContext) { expect(metrics?.task_claim).not.to.be(null); expect(metrics?.task_claim?.value).not.to.be(null); - expect(metrics?.task_claim?.value.success).to.equal(1); - expect(metrics?.task_claim?.value.total).to.equal(1); + expect(metrics?.task_claim?.value.success).to.be.greaterThan(0); + expect(metrics?.task_claim?.value.total).to.be.greaterThan(0); previousTaskClaimTimestamp = metrics?.task_claim?.timestamp!; diff --git a/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management.ts b/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management.ts index c7e9dc4536fb0..defb7763d89ce 100644 --- a/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management.ts +++ b/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management.ts @@ -797,7 +797,7 @@ export default function ({ getService }: FtrProviderContext) { await retry.try(async () => { const [scheduledTask] = (await currentTasks()).docs; expect(scheduledTask.id).to.eql(task.id); - expect(scheduledTask.status).to.eql('claiming'); + expect(['claiming', 'running'].includes(scheduledTask.status)).to.be(true); expect(scheduledTask.attempts).to.be.greaterThan(3); }); }); diff --git a/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management_removed_types.ts b/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management_removed_types.ts index e35c2c4730815..5c7ef55577861 100644 --- a/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management_removed_types.ts +++ b/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management_removed_types.ts @@ -77,12 +77,16 @@ export default function ({ getService }: FtrProviderContext) { } it('should successfully schedule registered tasks, not claim unregistered tasks and mark removed task types as unrecognized', async () => { + const testStart = new Date(); const scheduledTask = await scheduleTask({ taskType: 'sampleTask', schedule: { interval: `1s` }, params: {}, }); + let scheduledTaskRuns = 0; + let scheduledTaskInstanceRunAt = scheduledTask.runAt; + await retry.try(async () => { const tasks = (await currentTasks()).docs; expect(tasks.length).to.eql(3); @@ -98,8 +102,16 @@ export default function ({ getService }: FtrProviderContext) { ); const removedTaskInstance = tasks.find((task) => task.id === REMOVED_TASK_TYPE_ID); - expect(scheduledTaskInstance?.status).to.eql('claiming'); + if (scheduledTaskInstance && scheduledTaskInstance.runAt !== scheduledTaskInstanceRunAt) { + scheduledTaskRuns++; + scheduledTaskInstanceRunAt = scheduledTaskInstance.runAt; + } + + expect(scheduledTaskRuns).to.be.greaterThan(2); expect(unregisteredTaskInstance?.status).to.eql('idle'); + expect(new Date(unregisteredTaskInstance?.runAt || testStart).getTime()).to.be.lessThan( + testStart.getTime() + ); expect(removedTaskInstance?.status).to.eql('unrecognized'); }); }); diff --git a/x-pack/test/saved_object_tagging/functional/tests/visualize_integration.ts b/x-pack/test/saved_object_tagging/functional/tests/visualize_integration.ts index 61b7a2c1e9711..c4d0df07aeb64 100644 --- a/x-pack/test/saved_object_tagging/functional/tests/visualize_integration.ts +++ b/x-pack/test/saved_object_tagging/functional/tests/visualize_integration.ts @@ -32,14 +32,12 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { } await testSubjects.click('savedObjectTitle'); }; - // creates a simple markdown vis with a tag provided. + // creates a simple tsvb vis with a tag provided. const createSimpleMarkdownVis = async (opts: Record) => { - const { visName, visText, tagName } = opts; + const { visName, tagName } = opts; await PageObjects.visualize.navigateToNewVisualization(); - await PageObjects.visualize.clickMarkdownWidget(); - await PageObjects.visEditor.setMarkdownTxt(visText); - await PageObjects.visEditor.clickGo(); + await PageObjects.visualize.clickVisualBuilder(); await PageObjects.visualize.ensureSavePanelOpen(); await PageObjects.visualize.setSaveModalValues(visName, { @@ -150,10 +148,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await PageObjects.visualize.navigateToNewVisualization(); - await PageObjects.visualize.clickMarkdownWidget(); - await PageObjects.visEditor.setMarkdownTxt('Just some markdown'); - await PageObjects.visEditor.clickGo(); - + await PageObjects.visualize.clickVisualBuilder(); await PageObjects.visualize.ensureSavePanelOpen(); await PageObjects.visualize.setSaveModalValues('vis-with-new-tag', { saveAsNew: false, diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/engine.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/engine.ts index a7d32767f50ce..f51fbd15ceead 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/engine.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/engine.ts @@ -14,8 +14,7 @@ export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const utils = EntityStoreUtils(getService); - // Failing: See https://github.com/elastic/kibana/issues/196526 - describe.skip('@ess @skipInServerlessMKI Entity Store Engine APIs', () => { + describe('@ess @skipInServerlessMKI Entity Store Engine APIs', () => { const dataView = dataViewRouteHelpersFactory(supertest); before(async () => { @@ -33,22 +32,19 @@ export default ({ getService }: FtrProviderContext) => { }); it('should have installed the expected user resources', async () => { - await utils.initEntityEngineForEntityType('user'); + await utils.initEntityEngineForEntityTypesAndWait(['user']); await utils.expectEngineAssetsExist('user'); }); it('should have installed the expected host resources', async () => { - await utils.initEntityEngineForEntityType('host'); + await utils.initEntityEngineForEntityTypesAndWait(['host']); await utils.expectEngineAssetsExist('host'); }); }); describe('get and list', () => { before(async () => { - await Promise.all([ - utils.initEntityEngineForEntityType('host'), - utils.initEntityEngineForEntityType('user'), - ]); + await utils.initEntityEngineForEntityTypesAndWait(['host', 'user']); }); after(async () => { @@ -118,7 +114,7 @@ export default ({ getService }: FtrProviderContext) => { describe('start and stop', () => { before(async () => { - await utils.initEntityEngineForEntityType('host'); + await utils.initEntityEngineForEntityTypesAndWait(['host']); }); after(async () => { @@ -160,7 +156,7 @@ export default ({ getService }: FtrProviderContext) => { describe('delete', () => { it('should delete the host entity engine', async () => { - await utils.initEntityEngineForEntityType('host'); + await utils.initEntityEngineForEntityTypesAndWait(['host']); await api .deleteEntityEngine({ @@ -173,7 +169,7 @@ export default ({ getService }: FtrProviderContext) => { }); it('should delete the user entity engine', async () => { - await utils.initEntityEngineForEntityType('user'); + await utils.initEntityEngineForEntityTypesAndWait(['user']); await api .deleteEntityEngine({ @@ -188,7 +184,7 @@ export default ({ getService }: FtrProviderContext) => { describe('apply_dataview_indices', () => { before(async () => { - await utils.initEntityEngineForEntityType('host'); + await utils.initEntityEngineForEntityTypesAndWait(['host']); }); after(async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/engine_nondefault_spaces.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/engine_nondefault_spaces.ts index 481f7aa4056f6..64809533fec7b 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/engine_nondefault_spaces.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/engine_nondefault_spaces.ts @@ -18,8 +18,7 @@ export default ({ getService }: FtrProviderContextWithSpaces) => { const supertest = getService('supertest'); const utils = EntityStoreUtils(getService, namespace); - // Failing: See https://github.com/elastic/kibana/issues/196546 - describe.skip('@ess Entity Store Engine APIs in non-default space', () => { + describe('@ess Entity Store Engine APIs in non-default space', () => { const dataView = dataViewRouteHelpersFactory(supertest, namespace); before(async () => { @@ -43,22 +42,19 @@ export default ({ getService }: FtrProviderContextWithSpaces) => { }); it('should have installed the expected user resources', async () => { - await utils.initEntityEngineForEntityType('user'); + await utils.initEntityEngineForEntityTypesAndWait(['user']); await utils.expectEngineAssetsExist('user'); }); it('should have installed the expected host resources', async () => { - await utils.initEntityEngineForEntityType('host'); + await utils.initEntityEngineForEntityTypesAndWait(['host']); await utils.expectEngineAssetsExist('host'); }); }); describe('get and list', () => { before(async () => { - await Promise.all([ - utils.initEntityEngineForEntityType('host'), - utils.initEntityEngineForEntityType('user'), - ]); + await utils.initEntityEngineForEntityTypesAndWait(['host', 'user']); }); after(async () => { @@ -134,7 +130,7 @@ export default ({ getService }: FtrProviderContextWithSpaces) => { describe('start and stop', () => { before(async () => { - await utils.initEntityEngineForEntityType('host'); + await utils.initEntityEngineForEntityTypesAndWait(['host']); }); after(async () => { @@ -188,7 +184,7 @@ export default ({ getService }: FtrProviderContextWithSpaces) => { describe('delete', () => { it('should delete the host entity engine', async () => { - await utils.initEntityEngineForEntityType('host'); + await utils.initEntityEngineForEntityTypesAndWait(['host']); await api .deleteEntityEngine( @@ -204,7 +200,7 @@ export default ({ getService }: FtrProviderContextWithSpaces) => { }); it('should delete the user entity engine', async () => { - await utils.initEntityEngineForEntityType('user'); + await utils.initEntityEngineForEntityTypesAndWait(['user']); await api .deleteEntityEngine( diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/field_retention_operators.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/field_retention_operators.ts index ced0327194364..6e8f7dc3f6578 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/field_retention_operators.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/field_retention_operators.ts @@ -11,6 +11,7 @@ import { fieldOperatorToIngestProcessor, } from '@kbn/security-solution-plugin/server/lib/entity_analytics/entity_store/field_retention_definition'; import { FtrProviderContext } from '../../../../ftr_provider_context'; +import { applyIngestProcessorToDoc } from '../utils/ingest'; export default ({ getService }: FtrProviderContext) => { const es = getService('es'); const log = getService('log'); @@ -26,31 +27,8 @@ export default ({ getService }: FtrProviderContext) => { docSource: any ): Promise => { const step = fieldOperatorToIngestProcessor(operator, { enrichField: 'historical' }); - const doc = { - _index: 'index', - _id: 'id', - _source: docSource, - }; - - const res = await es.ingest.simulate({ - pipeline: { - description: 'test', - processors: [step], - }, - docs: [doc], - }); - - const firstDoc = res.docs?.[0]; - - // @ts-expect-error error is not in the types - const error = firstDoc?.error; - if (error) { - log.error('Full painless error below: '); - log.error(JSON.stringify(error, null, 2)); - throw new Error('Painless error running pipelie see logs for full detail : ' + error?.type); - } - return firstDoc?.doc?._source; + return applyIngestProcessorToDoc([step], docSource, es, log); }; describe('@ess @serverless @skipInServerlessMKI Entity store - Field Retention Pipeline Steps', () => { @@ -90,7 +68,7 @@ export default ({ getService }: FtrProviderContext) => { expectArraysMatchAnyOrder(resultDoc.test_field, ['foo']); }); - it('should take from history if latest field doesnt have maxLength values', async () => { + it("should take from history if latest field doesn't have maxLength values", async () => { const op: FieldRetentionOperator = { operation: 'collect_values', field: 'test_field', diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/utils/ingest.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/utils/ingest.ts new file mode 100644 index 0000000000000..24f7d759190b5 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/utils/ingest.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 { Client } from '@elastic/elasticsearch'; +import { IngestProcessorContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { ToolingLog } from '@kbn/tooling-log'; + +export const applyIngestProcessorToDoc = async ( + steps: IngestProcessorContainer[], + docSource: any, + es: Client, + log: ToolingLog +): Promise => { + const doc = { + _index: 'index', + _id: 'id', + _source: docSource, + }; + + const res = await es.ingest.simulate({ + pipeline: { + description: 'test', + processors: steps, + }, + docs: [doc], + }); + + const firstDoc = res.docs?.[0]; + + // @ts-expect-error error is not in the types + const error = firstDoc?.error; + if (error) { + log.error('Full painless error below: '); + log.error(JSON.stringify(error, null, 2)); + throw new Error('Painless error running pipeline see logs for full detail : ' + error?.type); + } + + return firstDoc?.doc?._source; +}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/init_and_status_apis.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/init_and_status_apis.ts index dd1fe34cd050a..b793ec1cb1306 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/init_and_status_apis.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/init_and_status_apis.ts @@ -27,19 +27,30 @@ export default ({ getService }: FtrProviderContext) => { const es = getService('es'); const supertest = getService('supertest'); const kibanaServer = getService('kibanaServer'); + const spaces = getService('spaces'); + const customSpaceName = 'ea-customspace-it'; const riskEngineRoutes = riskEngineRouteHelpersFactory(supertest); + const riskEngineRoutesWithNamespace = riskEngineRouteHelpersFactory(supertest, customSpaceName); const log = getService('log'); - // Failing: See https://github.com/elastic/kibana/issues/196319 - describe.skip('@ess @serverless @serverlessQA init_and_status_apis', () => { + describe('@ess @serverless @serverlessQA init_and_status_apis', () => { before(async () => { + await spaces.create({ + id: customSpaceName, + name: customSpaceName, + description: 'Space for ${customSpaceName}', + disabledFeatures: [], + }); await riskEngineRoutes.cleanUp(); + await riskEngineRoutesWithNamespace.cleanUp(); }); afterEach(async () => { await riskEngineRoutes.cleanUp(); + await riskEngineRoutesWithNamespace.cleanUp(); await clearLegacyTransforms({ es, log }); await clearLegacyDashboards({ supertest, log }); + await spaces.delete(customSpaceName); }); describe('init api', () => { @@ -54,10 +65,21 @@ export default ({ getService }: FtrProviderContext) => { risk_engine_resources_installed: true, }, }); + + const customNamespaceResponse = await riskEngineRoutesWithNamespace.init(); + expect(customNamespaceResponse.body).to.eql({ + result: { + errors: [], + legacy_risk_engine_disabled: true, + risk_engine_configuration_created: true, + risk_engine_enabled: true, + risk_engine_resources_installed: true, + }, + }); }); - it('should install resources on init call', async () => { - const componentTemplateName = '.risk-score-mappings'; + it('should install resources on init call in the default namespace', async () => { + const componentTemplateName = '.risk-score-mappings-default'; const indexTemplateName = '.risk-score.risk-score-default-index-template'; const dataStreamName = 'risk-score.risk-score-default'; const latestIndexName = 'risk-score.risk-score-latest-default'; @@ -210,7 +232,7 @@ export default ({ getService }: FtrProviderContext) => { expect(indexTemplate.index_template.index_patterns).to.eql([ 'risk-score.risk-score-default', ]); - expect(indexTemplate.index_template.composed_of).to.eql(['.risk-score-mappings']); + expect(indexTemplate.index_template.composed_of).to.eql(['.risk-score-mappings-default']); expect(indexTemplate.index_template.template!.mappings?.dynamic).to.eql(false); expect(indexTemplate.index_template.template!.mappings?._meta?.managed).to.eql(true); expect(indexTemplate.index_template.template!.mappings?._meta?.namespace).to.eql('default'); @@ -267,6 +289,221 @@ export default ({ getService }: FtrProviderContext) => { expect(transformStats.transforms[0].state).to.eql('stopped'); }); + it('should install resources on init call in the custom namespace', async () => { + const componentTemplateName = `.risk-score-mappings-${customSpaceName}`; + const indexTemplateName = `.risk-score.risk-score-${customSpaceName}-index-template`; + const dataStreamName = `risk-score.risk-score-${customSpaceName}`; + const latestIndexName = `risk-score.risk-score-latest-${customSpaceName}`; + const transformId = `risk_score_latest_transform_${customSpaceName}`; + + await riskEngineRoutesWithNamespace.init(); + + const { component_templates: componentTemplates1 } = await es.cluster.getComponentTemplate({ + name: componentTemplateName, + }); + + expect(componentTemplates1.length).to.eql(1); + const componentTemplate = componentTemplates1[0]; + + expect(componentTemplate.name).to.eql(componentTemplateName); + expect(componentTemplate.component_template.template.mappings).to.eql({ + dynamic: 'strict', + properties: { + '@timestamp': { + ignore_malformed: false, + type: 'date', + }, + host: { + properties: { + name: { + type: 'keyword', + }, + risk: { + properties: { + calculated_level: { + type: 'keyword', + }, + calculated_score: { + type: 'float', + }, + calculated_score_norm: { + type: 'float', + }, + category_1_count: { + type: 'long', + }, + category_1_score: { + type: 'float', + }, + id_field: { + type: 'keyword', + }, + id_value: { + type: 'keyword', + }, + notes: { + type: 'keyword', + }, + inputs: { + properties: { + id: { + type: 'keyword', + }, + index: { + type: 'keyword', + }, + category: { + type: 'keyword', + }, + description: { + type: 'keyword', + }, + risk_score: { + type: 'float', + }, + timestamp: { + type: 'date', + }, + }, + type: 'object', + }, + }, + type: 'object', + }, + }, + }, + user: { + properties: { + name: { + type: 'keyword', + }, + risk: { + properties: { + calculated_level: { + type: 'keyword', + }, + calculated_score: { + type: 'float', + }, + calculated_score_norm: { + type: 'float', + }, + category_1_count: { + type: 'long', + }, + category_1_score: { + type: 'float', + }, + id_field: { + type: 'keyword', + }, + id_value: { + type: 'keyword', + }, + notes: { + type: 'keyword', + }, + inputs: { + properties: { + id: { + type: 'keyword', + }, + index: { + type: 'keyword', + }, + category: { + type: 'keyword', + }, + description: { + type: 'keyword', + }, + risk_score: { + type: 'float', + }, + timestamp: { + type: 'date', + }, + }, + type: 'object', + }, + }, + type: 'object', + }, + }, + }, + }, + }); + + const { index_templates: indexTemplates } = await es.indices.getIndexTemplate({ + name: indexTemplateName, + }); + expect(indexTemplates.length).to.eql(1); + const indexTemplate = indexTemplates[0]; + expect(indexTemplate.name).to.eql(indexTemplateName); + expect(indexTemplate.index_template.index_patterns).to.eql([ + `risk-score.risk-score-${customSpaceName}`, + ]); + expect(indexTemplate.index_template.composed_of).to.eql([ + `.risk-score-mappings-${customSpaceName}`, + ]); + expect(indexTemplate.index_template.template!.mappings?.dynamic).to.eql(false); + expect(indexTemplate.index_template.template!.mappings?._meta?.managed).to.eql(true); + expect(indexTemplate.index_template.template!.mappings?._meta?.namespace).to.eql( + customSpaceName + ); + expect(indexTemplate.index_template.template!.mappings?._meta?.kibana?.version).to.be.a( + 'string' + ); + + expect(indexTemplate.index_template.template!.settings).to.eql({ + index: { + mapping: { + total_fields: { + limit: '1000', + }, + }, + }, + }); + + expect(indexTemplate.index_template.template!.lifecycle).to.eql({ + enabled: true, + }); + + const dsResponse = await es.indices.get({ + index: dataStreamName, + }); + + const dataStream = Object.values(dsResponse).find( + (ds) => ds.data_stream === dataStreamName + ); + + expect(dataStream?.mappings?._meta?.managed).to.eql(true); + expect(dataStream?.mappings?._meta?.namespace).to.eql(customSpaceName); + expect(dataStream?.mappings?._meta?.kibana?.version).to.be.a('string'); + expect(dataStream?.mappings?.dynamic).to.eql('false'); + + expect(dataStream?.settings?.index?.mapping).to.eql({ + total_fields: { + limit: '1000', + }, + }); + + expect(dataStream?.settings?.index?.hidden).to.eql('true'); + expect(dataStream?.settings?.index?.number_of_shards).to.eql(1); + + const indexExist = await es.indices.exists({ + index: latestIndexName, + }); + + expect(indexExist).to.eql(true); + + const transformStats = await es.transform.getTransformStats({ + transform_id: transformId, + }); + + expect(transformStats.transforms[0].state).to.eql('stopped'); + }); + it('should create configuration saved object', async () => { await riskEngineRoutes.init(); const response = await kibanaServer.savedObjects.find({ diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/elastic_asset_checker.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/elastic_asset_checker.ts index 8e8635cefc26b..c0dddb4ddb093 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/elastic_asset_checker.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/elastic_asset_checker.ts @@ -9,6 +9,8 @@ import { FtrProviderContext } from '@kbn/ftr-common-functional-services'; export const elasticAssetCheckerFactory = (getService: FtrProviderContext['getService']) => { const es = getService('es'); + const retry = getService('retry'); + const log = getService('log'); const expectTransformExists = async (transformId: string) => { return expectTransformStatus(transformId, true); @@ -18,45 +20,43 @@ export const elasticAssetCheckerFactory = (getService: FtrProviderContext['getSe return expectTransformStatus(transformId, false); }; - const expectTransformStatus = async ( - transformId: string, - exists: boolean, - attempts: number = 5, - delayMs: number = 2000 - ) => { - let currentAttempt = 1; - while (currentAttempt <= attempts) { - try { - await es.transform.getTransform({ transform_id: transformId }); - if (!exists) { - throw new Error(`Expected transform ${transformId} to not exist, but it does`); + const expectTransformStatus = async (transformId: string, exists: boolean) => { + await retry.waitForWithTimeout( + `transform ${transformId} to ${exists ? 'exist' : 'not exist'}`, + 10_000, + async () => { + try { + await es.transform.getTransform({ transform_id: transformId }); + return exists; + } catch (e) { + log.debug(`Transform ${transformId} not found: ${e}`); + return !exists; } - return; // Transform exists, exit the loop - } catch (e) { - if (currentAttempt === attempts) { - if (exists) { - throw new Error(`Expected transform ${transformId} to exist, but it does not: ${e}`); - } else { - return; // Transform does not exist, exit the loop - } - } - await new Promise((resolve) => setTimeout(resolve, delayMs)); - currentAttempt++; } - } + ); }; const expectEnrichPolicyStatus = async (policyId: string, exists: boolean) => { - try { - await es.enrich.getPolicy({ name: policyId }); - if (!exists) { - throw new Error(`Expected enrich policy ${policyId} to not exist, but it does`); - } - } catch (e) { - if (exists) { - throw new Error(`Expected enrich policy ${policyId} to exist, but it does not: ${e}`); + await retry.waitForWithTimeout( + `enrich policy ${policyId} to ${exists ? 'exist' : 'not exist'}`, + 20_000, + async () => { + try { + const res = await es.enrich.getPolicy({ name: policyId }); + const policy = res.policies?.[0]; + if (policy) { + log.debug(`Enrich policy ${policyId} found: ${JSON.stringify(res)}`); + return exists; + } else { + log.debug(`Enrich policy ${policyId} not found: ${JSON.stringify(res)}`); + return !exists; + } + } catch (e) { + log.debug(`Enrich policy ${policyId} not found: ${e}`); + return !exists; + } } - } + ); }; const expectEnrichPolicyExists = async (policyId: string) => @@ -66,18 +66,19 @@ export const elasticAssetCheckerFactory = (getService: FtrProviderContext['getSe expectEnrichPolicyStatus(policyId, false); const expectComponentTemplatStatus = async (templateName: string, exists: boolean) => { - try { - await es.cluster.getComponentTemplate({ name: templateName }); - if (!exists) { - throw new Error(`Expected component template ${templateName} to not exist, but it does`); - } - } catch (e) { - if (exists) { - throw new Error( - `Expected component template ${templateName} to exist, but it does not: ${e}` - ); + await retry.waitForWithTimeout( + `component template ${templateName} to ${exists ? 'exist' : 'not exist'}`, + 10_000, + async () => { + try { + await es.cluster.getComponentTemplate({ name: templateName }); + return exists; // Component template exists + } catch (e) { + log.debug(`Component template ${templateName} not found: ${e}`); + return !exists; // Component template does not exist + } } - } + ); }; const expectComponentTemplateExists = async (templateName: string) => @@ -87,23 +88,45 @@ export const elasticAssetCheckerFactory = (getService: FtrProviderContext['getSe expectComponentTemplatStatus(templateName, false); const expectIngestPipelineStatus = async (pipelineId: string, exists: boolean) => { + await retry.waitForWithTimeout( + `ingest pipeline ${pipelineId} to ${exists ? 'exist' : 'not exist'}`, + 10_000, + async () => { + try { + await es.ingest.getPipeline({ id: pipelineId }); + return exists; // Ingest pipeline exists + } catch (e) { + log.debug(`Ingest pipeline ${pipelineId} not found: ${e}`); + return !exists; // Ingest pipeline does not exist + } + } + ); + }; + + const expectIngestPipelineExists = async (pipelineId: string) => + expectIngestPipelineStatus(pipelineId, true); + + const expectIngestPipelineNotFound = async (pipelineId: string) => + expectIngestPipelineStatus(pipelineId, false); + + const expectIndexStatus = async (indexName: string, exists: boolean) => { try { - await es.ingest.getPipeline({ id: pipelineId }); + await es.indices.get({ index: indexName }); if (!exists) { - throw new Error(`Expected ingest pipeline ${pipelineId} to not exist, but it does`); + throw new Error(`Expected index ${indexName} to not exist, but it does`); } } catch (e) { if (exists) { - throw new Error(`Expected ingest pipeline ${pipelineId} to exist, but it does not: ${e}`); + throw new Error(`Expected index ${indexName} to exist, but it does not: ${e}`); } } }; - const expectIngestPipelineExists = async (pipelineId: string) => - expectIngestPipelineStatus(pipelineId, true); + const expectEntitiesIndexExists = async (entityType: string, namespace: string) => + expectIndexStatus(`.entities.v1.latest.security_${entityType}_${namespace}`, true); - const expectIngestPipelineNotFound = async (pipelineId: string) => - expectIngestPipelineStatus(pipelineId, false); + const expectEntitiesIndexNotFound = async (entityType: string, namespace: string) => + expectIndexStatus(`.entities.v1.latest.security_${entityType}_${namespace}`, false); return { expectComponentTemplateExists, @@ -112,6 +135,8 @@ export const elasticAssetCheckerFactory = (getService: FtrProviderContext['getSe expectEnrichPolicyNotFound, expectIngestPipelineExists, expectIngestPipelineNotFound, + expectEntitiesIndexExists, + expectEntitiesIndexNotFound, expectTransformExists, expectTransformNotFound, }; diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/entity_store.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/entity_store.ts index 24c1434b5e4a5..029103425af68 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/entity_store.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/entity_store.ts @@ -17,6 +17,7 @@ export const EntityStoreUtils = ( const api = getService('securitySolutionApi'); const es = getService('es'); const log = getService('log'); + const retry = getService('retry'); const { expectTransformExists, expectTransformNotFound, @@ -26,6 +27,8 @@ export const EntityStoreUtils = ( expectComponentTemplateNotFound, expectIngestPipelineExists, expectIngestPipelineNotFound, + expectEntitiesIndexExists, + expectEntitiesIndexNotFound, } = elasticAssetCheckerFactory(getService); log.debug(`EntityStoreUtils namespace: ${namespace}`); @@ -48,7 +51,7 @@ export const EntityStoreUtils = ( } }; - const initEntityEngineForEntityType = async (entityType: EntityType) => { + const _initEntityEngineForEntityType = async (entityType: EntityType) => { log.info( `Initializing engine for entity type ${entityType} in namespace ${namespace || 'default'}` ); @@ -68,6 +71,22 @@ export const EntityStoreUtils = ( expect(res.status).to.eql(200); }; + const initEntityEngineForEntityTypesAndWait = async (entityTypes: EntityType[]) => { + await Promise.all(entityTypes.map((entityType) => _initEntityEngineForEntityType(entityType))); + + await retry.waitForWithTimeout( + `Engines to start for entity types: ${entityTypes.join(', ')}`, + 60_000, + async () => { + const { body } = await api.listEntityEngines(namespace).expect(200); + if (body.engines.every((engine: any) => engine.status === 'started')) { + return true; + } + return false; + } + ); + }; + const expectTransformStatus = async ( transformId: string, exists: boolean, @@ -98,21 +117,27 @@ export const EntityStoreUtils = ( const expectEngineAssetsExist = async (entityType: EntityType) => { await expectTransformExists(`entities-v1-latest-security_${entityType}_${namespace}`); - await expectEnrichPolicyExists(`entity_store_field_retention_${entityType}_${namespace}_v1`); + await expectEnrichPolicyExists( + `entity_store_field_retention_${entityType}_${namespace}_v1.0.0` + ); await expectComponentTemplateExists(`security_${entityType}_${namespace}-latest@platform`); await expectIngestPipelineExists(`security_${entityType}_${namespace}-latest@platform`); + await expectEntitiesIndexExists(entityType, namespace); }; const expectEngineAssetsDoNotExist = async (entityType: EntityType) => { await expectTransformNotFound(`entities-v1-latest-security_${entityType}_${namespace}`); - await expectEnrichPolicyNotFound(`entity_store_field_retention_${entityType}_${namespace}_v1`); + await expectEnrichPolicyNotFound( + `entity_store_field_retention_${entityType}_${namespace}_v1.0.0` + ); await expectComponentTemplateNotFound(`security_${entityType}_${namespace}-latest@platform`); await expectIngestPipelineNotFound(`security_${entityType}_${namespace}-latest@platform`); + await expectEntitiesIndexNotFound(entityType, namespace); }; return { cleanEngines, - initEntityEngineForEntityType, + initEntityEngineForEntityTypesAndWait, expectTransformStatus, expectEngineAssetsExist, expectEngineAssetsDoNotExist, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/exceptions/shared_exception_lists_management/list_detail_page/list_details.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/exceptions/shared_exception_lists_management/list_detail_page/list_details.cy.ts index 2a98a12cba7e5..5b2326ec617a6 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/exceptions/shared_exception_lists_management/list_detail_page/list_details.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/exceptions/shared_exception_lists_management/list_detail_page/list_details.cy.ts @@ -41,8 +41,7 @@ const getExceptionList1 = () => ({ const EXCEPTION_LIST_NAME = 'Newly created list'; -// FLAKY: https://github.com/elastic/kibana/issues/180740 -describe.skip('Exception list detail page', { tags: ['@ess', '@serverless'] }, () => { +describe('Exception list detail page', { tags: ['@ess', '@serverless'] }, () => { beforeEach(() => { login(); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_edit/indicator_match_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_edit/indicator_match_rule.cy.ts index fe616f6ba1969..648fa51f0abb1 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_edit/indicator_match_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_edit/indicator_match_rule.cy.ts @@ -58,7 +58,8 @@ describe( }); // https://github.com/elastic/kibana/issues/187621 - describe('without suppression', { tags: ['@skipInServerlessMKI'] }, () => { + // FLAKY: https://github.com/elastic/kibana/issues/196711 + describe.skip('without suppression', { tags: ['@skipInServerlessMKI'] }, () => { beforeEach(() => { createRule(rule); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/snoozing/rule_snoozing.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/snoozing/rule_snoozing.cy.ts index 270a9146f65a9..9622cb94bdd4f 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/snoozing/rule_snoozing.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/snoozing/rule_snoozing.cy.ts @@ -217,7 +217,7 @@ describe('rule snoozing', { tags: ['@ess', '@serverless', '@skipInServerlessMKI' describe('Handling errors', () => { it('shows an error if unable to load snooze settings', () => { createRule(getNewRule({ name: 'Test rule', enabled: false })).then(({ body: rule }) => { - cy.intercept('GET', `${INTERNAL_ALERTING_API_FIND_RULES_PATH}*`, { + cy.intercept('POST', `${INTERNAL_ALERTING_API_FIND_RULES_PATH}*`, { statusCode: 500, }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/asset_criticality_upload_page.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/asset_criticality_upload_page.cy.ts index ecade2f02ce22..94030267b3860 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/asset_criticality_upload_page.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/asset_criticality_upload_page.cy.ts @@ -17,6 +17,7 @@ import { login } from '../../tasks/login'; import { visit } from '../../tasks/navigation'; import { ENTITY_ANALYTICS_ASSET_CRITICALITY_URL } from '../../urls/navigation'; +// Failing: See https://github.com/elastic/kibana/issues/196563 // Failing: See https://github.com/elastic/kibana/issues/196563 describe.skip( 'Asset Criticality Upload page', diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/unified_components/table_row_actions.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/unified_components/table_row_actions.cy.ts index 6942571374da5..4196b1e765e98 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/unified_components/table_row_actions.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/unified_components/table_row_actions.cy.ts @@ -21,7 +21,8 @@ import { } from '../../../../tasks/timeline'; import { ALERTS_URL } from '../../../../urls/navigation'; -describe( +// FLAKY: https://github.com/elastic/kibana/issues/196851 +describe.skip( 'Unified Timeline table Row Actions', { tags: ['@ess', '@serverless', '@skipInServerlessMKI'], diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/create_new_rule.ts b/x-pack/test/security_solution_cypress/cypress/tasks/create_new_rule.ts index 501dd0461dd44..e9822d1340a85 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/create_new_rule.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/create_new_rule.ts @@ -884,7 +884,7 @@ export const waitForAlertsToPopulate = (alertCountThreshold = 1) => { return alertCount >= alertCountThreshold; }); }, - { interval: 500, timeout: 12000 } + { interval: 500, timeout: 30000 } ); waitForAlerts(); }; diff --git a/x-pack/test/security_solution_endpoint/apps/integrations/endpoint_exceptions.ts b/x-pack/test/security_solution_endpoint/apps/integrations/endpoint_exceptions.ts index 5594ee2ad7cd7..42d28132998bf 100644 --- a/x-pack/test/security_solution_endpoint/apps/integrations/endpoint_exceptions.ts +++ b/x-pack/test/security_solution_endpoint/apps/integrations/endpoint_exceptions.ts @@ -31,6 +31,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const MINUTES = 60 * 1000 * 10; // FLAKY: https://github.com/elastic/kibana/issues/173441 + // Failing: See https://github.com/elastic/kibana/issues/173441 describe.skip('Endpoint Exceptions', function () { targetTags(this, ['@ess', '@serverless']); diff --git a/x-pack/test/task_manager_claimer_mget/plugins/sample_task_plugin_mget/server/init_routes.ts b/x-pack/test/task_manager_claimer_mget/plugins/sample_task_plugin_mget/server/init_routes.ts index 1c346584beaf2..f1e697399fe09 100644 --- a/x-pack/test/task_manager_claimer_mget/plugins/sample_task_plugin_mget/server/init_routes.ts +++ b/x-pack/test/task_manager_claimer_mget/plugins/sample_task_plugin_mget/server/init_routes.ts @@ -371,6 +371,7 @@ export function initRoutes( do { const { docs: tasks } = await taskManager.fetch({ query: taskManagerQuery, + size: 1000, }); tasksFound = tasks.length; await Promise.all(tasks.map((task) => taskManager.remove(task.id))); diff --git a/x-pack/test/task_manager_claimer_mget/test_suites/task_manager/health_route.ts b/x-pack/test/task_manager_claimer_mget/test_suites/task_manager/health_route.ts index 241bd8adcd40d..23e387061830a 100644 --- a/x-pack/test/task_manager_claimer_mget/test_suites/task_manager/health_route.ts +++ b/x-pack/test/task_manager_claimer_mget/test_suites/task_manager/health_route.ts @@ -6,9 +6,7 @@ */ import expect from '@kbn/expect'; -import url from 'url'; import { keyBy, mapValues } from 'lodash'; -import supertest from 'supertest'; import { ConcreteTaskInstance } from '@kbn/task-manager-plugin/server'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -82,14 +80,13 @@ interface MonitoringStats { } export default function ({ getService }: FtrProviderContext) { - const config = getService('config'); const retry = getService('retry'); - const request = supertest(url.format(config.get('servers.kibana'))); + const supertest = getService('supertest'); const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); function getHealthRequest() { - return request.get('/api/task_manager/_health').set('kbn-xsrf', 'foo'); + return supertest.get('/api/task_manager/_health').set('kbn-xsrf', 'foo'); } function getHealth(): Promise { @@ -114,7 +111,7 @@ export default function ({ getService }: FtrProviderContext) { } function scheduleTask(task: Partial): Promise { - return request + return supertest .post('/api/sample_tasks/schedule') .set('kbn-xsrf', 'xxx') .send({ task }) @@ -125,6 +122,11 @@ export default function ({ getService }: FtrProviderContext) { const monitoredAggregatedStatsRefreshRate = 5000; describe('health', () => { + afterEach(async () => { + // clean up after each test + return await supertest.delete('/api/sample_tasks').set('kbn-xsrf', 'xxx').expect(200); + }); + it('should return basic configuration of task manager', async () => { const health = await getHealth(); expect(health.status).to.eql('OK'); diff --git a/x-pack/test/task_manager_claimer_mget/test_suites/task_manager/task_management.ts b/x-pack/test/task_manager_claimer_mget/test_suites/task_manager/task_management.ts index 6323cef329ed6..f03023fb10ee8 100644 --- a/x-pack/test/task_manager_claimer_mget/test_suites/task_manager/task_management.ts +++ b/x-pack/test/task_manager_claimer_mget/test_suites/task_manager/task_management.ts @@ -469,8 +469,7 @@ export default function ({ getService }: FtrProviderContext) { }); }); - // always failing - it.skip('should only run as many instances of a task as its maxConcurrency will allow', async () => { + it('should only run as many instances of a task as its maxConcurrency will allow', async () => { // should run as there's only one and maxConcurrency on this TaskType is 1 const firstWithSingleConcurrency = await scheduleTask({ taskType: 'sampleTaskWithSingleConcurrency', @@ -762,18 +761,24 @@ export default function ({ getService }: FtrProviderContext) { }); }); - // flaky - it.skip('should continue claiming recurring task even if maxAttempts has been reached', async () => { + it('should continue claiming recurring task even if maxAttempts has been reached', async () => { const task = await scheduleTask({ taskType: 'sampleRecurringTaskTimingOut', schedule: { interval: '1s' }, params: {}, }); + let taskRuns = 0; + let taskRetryAt = task.retryAt; + await retry.try(async () => { const [scheduledTask] = (await currentTasks()).docs; expect(scheduledTask.id).to.eql(task.id); - expect(scheduledTask.status).to.eql('claiming'); + if (scheduledTask.retryAt !== taskRetryAt) { + taskRuns++; + taskRetryAt = scheduledTask.retryAt; + } + expect(taskRuns).to.be.greaterThan(3); expect(scheduledTask.attempts).to.be.greaterThan(3); }); }); diff --git a/x-pack/test/task_manager_claimer_mget/test_suites/task_manager/task_management_removed_types.ts b/x-pack/test/task_manager_claimer_mget/test_suites/task_manager/task_management_removed_types.ts index e13615cceab0c..aae90a52572c7 100644 --- a/x-pack/test/task_manager_claimer_mget/test_suites/task_manager/task_management_removed_types.ts +++ b/x-pack/test/task_manager_claimer_mget/test_suites/task_manager/task_management_removed_types.ts @@ -56,6 +56,11 @@ export default function ({ getService }: FtrProviderContext) { await esArchiver.unload('x-pack/test/functional/es_archives/task_manager_removed_types'); }); + afterEach(async () => { + // clean up after each test + return await request.delete('/api/sample_tasks').set('kbn-xsrf', 'xxx').expect(200); + }); + function scheduleTask( task: Partial ): Promise { @@ -76,14 +81,17 @@ export default function ({ getService }: FtrProviderContext) { .then((response) => response.body); } - // flaky - it.skip('should successfully schedule registered tasks, not claim unregistered tasks and mark removed task types as unrecognized', async () => { + it('should successfully schedule registered tasks, not claim unregistered tasks and mark removed task types as unrecognized', async () => { + const testStart = new Date(); const scheduledTask = await scheduleTask({ taskType: 'sampleTask', schedule: { interval: `1s` }, params: {}, }); + let scheduledTaskRuns = 0; + let scheduledTaskInstanceRunAt = scheduledTask.runAt; + await retry.try(async () => { const tasks = (await currentTasks()).docs; expect(tasks.length).to.eql(3); @@ -99,8 +107,16 @@ export default function ({ getService }: FtrProviderContext) { ); const removedTaskInstance = tasks.find((task) => task.id === REMOVED_TASK_TYPE_ID); - expect(scheduledTaskInstance?.status).to.eql('claiming'); + if (scheduledTaskInstance && scheduledTaskInstance.runAt !== scheduledTaskInstanceRunAt) { + scheduledTaskRuns++; + scheduledTaskInstanceRunAt = scheduledTaskInstance.runAt; + } + + expect(scheduledTaskRuns).to.be.greaterThan(2); expect(unregisteredTaskInstance?.status).to.eql('idle'); + expect(new Date(unregisteredTaskInstance?.runAt || testStart).getTime()).to.be.lessThan( + testStart.getTime() + ); expect(removedTaskInstance?.status).to.eql('unrecognized'); }); }); diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json index 03fbddf161f00..2ba14ceb1218c 100644 --- a/x-pack/test/tsconfig.json +++ b/x-pack/test/tsconfig.json @@ -185,6 +185,8 @@ "@kbn/cloud-security-posture-common", "@kbn/saved-objects-management-plugin", "@kbn/alerting-types", - "@kbn/ai-assistant-common" + "@kbn/ai-assistant-common", + "@kbn/core-deprecations-common", + "@kbn/usage-collection-plugin" ] } diff --git a/x-pack/test/upgrade_assistant_integration/config.js b/x-pack/test/upgrade_assistant_integration/config.ts similarity index 70% rename from x-pack/test/upgrade_assistant_integration/config.js rename to x-pack/test/upgrade_assistant_integration/config.ts index 9529e4bc568d3..0794f4d0b9ada 100644 --- a/x-pack/test/upgrade_assistant_integration/config.js +++ b/x-pack/test/upgrade_assistant_integration/config.ts @@ -6,8 +6,10 @@ */ import { commonFunctionalServices } from '@kbn/ftr-common-functional-services'; +import { FtrConfigProviderContext } from '@kbn/test'; +import path from 'node:path'; -export default async function ({ readConfigFile }) { +export default async function ({ readConfigFile }: FtrConfigProviderContext) { // Read the Kibana API integration tests config file so that we can utilize its services. const kibanaAPITestsConfig = await readConfigFile( require.resolve('@kbn/test-suites-src/api_integration/config') @@ -26,7 +28,14 @@ export default async function ({ readConfigFile }) { junit: { reportName: 'X-Pack Upgrade Assistant Integration Tests', }, - kbnTestServer: xPackFunctionalTestsConfig.get('kbnTestServer'), + kbnTestServer: { + ...xPackFunctionalTestsConfig.get('kbnTestServer'), + serverArgs: [ + ...xPackFunctionalTestsConfig.get('kbnTestServer.serverArgs'), + `--plugin-path=${path.resolve(__dirname, '../../../examples/routing_example')}`, + `--plugin-path=${path.resolve(__dirname, '../../../examples/developer_examples')}`, + ], + }, esTestCluster: { ...xPackFunctionalTestsConfig.get('esTestCluster'), // this archive can not be loaded into 8.0+ diff --git a/x-pack/test/upgrade_assistant_integration/upgrade_assistant/api_deprecations.ts b/x-pack/test/upgrade_assistant_integration/upgrade_assistant/api_deprecations.ts new file mode 100644 index 0000000000000..f146bf38f5f26 --- /dev/null +++ b/x-pack/test/upgrade_assistant_integration/upgrade_assistant/api_deprecations.ts @@ -0,0 +1,162 @@ +/* + * Copyright 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 expect from '@kbn/expect'; +import { expect as expectExpect } from 'expect'; +import type { DomainDeprecationDetails } from '@kbn/core-deprecations-common'; +import { ApiDeprecationDetails } from '@kbn/core-deprecations-common/src/types'; +import { setTimeout as setTimeoutAsync } from 'timers/promises'; +import { UsageCountersSavedObject } from '@kbn/usage-collection-plugin/server'; +import _ from 'lodash'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; + +interface DomainApiDeprecationDetails extends ApiDeprecationDetails { + domainId: string; +} + +const getApiDeprecations = (allDeprecations: DomainDeprecationDetails[]) => { + return allDeprecations.filter( + (deprecation) => deprecation.deprecationType === 'api' + ) as unknown as DomainApiDeprecationDetails[]; +}; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const retry = getService('retry'); + const es = getService('es'); + + describe('Kibana API Deprecations', () => { + before(async () => { + // await kibanaServer.savedObjects.cleanStandardList(); + await esArchiver.emptyKibanaIndex(); + }); + it('returns does not return api deprecations if the routes are not called', async () => { + const { deprecations } = (await supertest.get(`/api/deprecations/`).expect(200)).body; + const apiDeprecations = getApiDeprecations(deprecations); + expect(apiDeprecations.length).to.equal(0); + }); + + it('returns deprecated APIs when the api is called', async () => { + await supertest.get(`/api/routing_example/d/removed_route`).expect(200); + + // sleep a little until the usage counter is synced into ES + await setTimeoutAsync(3000); + await retry.tryForTime( + 15 * 1000, + async () => { + const { deprecations } = (await supertest.get(`/api/deprecations/`).expect(200)).body; + const apiDeprecations = getApiDeprecations(deprecations); + expect(apiDeprecations.length).to.equal(1); + + expectExpect(apiDeprecations[0].correctiveActions.mark_as_resolved_api).toEqual({ + routePath: '/api/routing_example/d/removed_route', + routeMethod: 'get', + apiTotalCalls: 1, + totalMarkedAsResolved: 0, + timestamp: expectExpect.any(String), + }); + + expectExpect(apiDeprecations[0].domainId).toEqual('core.api_deprecations'); + expectExpect(apiDeprecations[0].apiId).toEqual( + 'unversioned|get|/api/routing_example/d/removed_route' + ); + expectExpect(apiDeprecations[0].title).toEqual( + 'The "GET /api/routing_example/d/removed_route" route is removed' + ); + }, + undefined, + 2000 + ); + }); + + it('no longer returns deprecated API when it is marked as resolved', async () => { + await supertest + .post(`/api/deprecations/mark_as_resolved`) + .set('kbn-xsrf', 'xxx') + .send({ + domainId: 'core.api_deprecations', + routePath: '/api/routing_example/d/removed_route', + routeMethod: 'get', + incrementBy: 1, + }) + .expect(200); + + // sleep a little until the usage counter is synced into ES + await setTimeoutAsync(5000); + await retry.tryForTime(15 * 1000, async () => { + const { deprecations } = (await supertest.get(`/api/deprecations/`).expect(200)).body; + const apiDeprecations = getApiDeprecations(deprecations); + expect(apiDeprecations.length).to.equal(0); + }); + }); + + it('returns deprecated API when it is called again after resolved, but with a different message', async () => { + await supertest.get(`/api/routing_example/d/removed_route`).expect(200); + + // sleep a little until the usage counter is synced into ES + await setTimeoutAsync(3000); + await retry.tryForTime( + 15 * 1000, + async () => { + const { deprecations } = (await supertest.get(`/api/deprecations/`).expect(200)).body; + const apiDeprecations = getApiDeprecations(deprecations); + expect(apiDeprecations.length).to.equal(1); + + expectExpect(apiDeprecations[0].correctiveActions.mark_as_resolved_api).toEqual({ + routePath: '/api/routing_example/d/removed_route', + routeMethod: 'get', + apiTotalCalls: 2, + totalMarkedAsResolved: 1, + timestamp: expectExpect.any(String), + }); + }, + undefined, + 2000 + ); + }); + + it('keeps track of all counters via saved objects and core usage counters', async () => { + const should = ['total', 'resolved', 'marked_as_resolved'].map((type) => ({ + match: { 'usage-counter.counterType': `deprecated_api_call:${type}` }, + })); + + const { hits } = await es.search<{ 'usage-counter': UsageCountersSavedObject }>({ + index: '.kibana_usage_counters', + body: { + query: { bool: { should } }, + }, + }); + + expect(hits.hits.length).to.equal(3); + const counters = hits.hits.map((hit) => hit._source!['usage-counter']).sort(); + expectExpect(_.sortBy(counters, 'counterType')).toEqual([ + { + count: 1, + counterName: 'unversioned|get|/api/routing_example/d/removed_route', + counterType: 'deprecated_api_call:marked_as_resolved', + domainId: 'core', + source: 'server', + }, + { + count: 1, + counterName: 'unversioned|get|/api/routing_example/d/removed_route', + counterType: 'deprecated_api_call:resolved', + domainId: 'core', + source: 'server', + }, + { + count: 2, + counterName: 'unversioned|get|/api/routing_example/d/removed_route', + counterType: 'deprecated_api_call:total', + domainId: 'core', + source: 'server', + }, + ]); + }); + }); +} diff --git a/x-pack/test/upgrade_assistant_integration/upgrade_assistant/index.js b/x-pack/test/upgrade_assistant_integration/upgrade_assistant/index.ts similarity index 64% rename from x-pack/test/upgrade_assistant_integration/upgrade_assistant/index.js rename to x-pack/test/upgrade_assistant_integration/upgrade_assistant/index.ts index eb09d24b79b6a..2aaddc7d6f669 100644 --- a/x-pack/test/upgrade_assistant_integration/upgrade_assistant/index.js +++ b/x-pack/test/upgrade_assistant_integration/upgrade_assistant/index.ts @@ -5,8 +5,11 @@ * 2.0. */ -export default function ({ loadTestFile }) { +import { FtrProviderContext } from '../../common/ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { describe('upgrade assistant', function () { loadTestFile(require.resolve('./reindexing')); + loadTestFile(require.resolve('./api_deprecations')); }); } diff --git a/x-pack/test_serverless/api_integration/config.base.ts b/x-pack/test_serverless/api_integration/config.base.ts index 2b247836dd627..5d8fb117fe8e5 100644 --- a/x-pack/test_serverless/api_integration/config.base.ts +++ b/x-pack/test_serverless/api_integration/config.base.ts @@ -36,10 +36,6 @@ export function createTestConfig(options: CreateTestConfigOptions) { serverArgs: [ ...svlSharedConfig.get('kbnTestServer.serverArgs'), `--serverless=${options.serverlessProject}`, - // custom native roles are enabled only for search and security projects - ...(options.serverlessProject !== 'oblt' - ? ['--xpack.security.roleManagementEnabled=true'] - : []), ...(options.kbnServerArgs || []), ], }, diff --git a/x-pack/test_serverless/api_integration/test_suites/common/management/multiple_spaces_enabled.ts b/x-pack/test_serverless/api_integration/test_suites/common/management/multiple_spaces_enabled.ts deleted file mode 100644 index f90539f0cbfef..0000000000000 --- a/x-pack/test_serverless/api_integration/test_suites/common/management/multiple_spaces_enabled.ts +++ /dev/null @@ -1,410 +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 expect from 'expect'; -import { asyncForEach } from '@kbn/std'; -import { SupertestWithRoleScopeType } from '@kbn/test-suites-xpack/api_integration/deployment_agnostic/services'; -import { FtrProviderContext } from '../../../ftr_provider_context'; - -// Notes: -// This suite is currently only called from the feature flags test configs, e.g. -// x-pack/test_serverless/api_integration/test_suites/search/config.feature_flags.ts -// Configuration toggle: -// kbnServerArgs: ['--xpack.spaces.maxSpaces=100'], -// -// Initial test coverage limited to CRUD operations and ensuring disabling features/toggling feature visibility is not possible. -// Full coverage of x-pack/test/api_integration/apis/spaces & x-pack/test/spaces_api_integration -// should be converted into a deployment agnostic suite when spaces are -// permanently enabled in serverless. -// -// The route access tests for the spaces APIs in ./spaces.ts should also get updated at that time. - -export default function ({ getService }: FtrProviderContext) { - const svlCommonApi = getService('svlCommonApi'); - const roleScopedSupertest = getService('roleScopedSupertest'); - const samlAuth = getService('samlAuth'); - - // CRUD operations to become public APIs: https://github.com/elastic/kibana/issues/192153 - let supertestAdminWithApiKey: SupertestWithRoleScopeType; - let supertestAdminWithCookieCredentials: SupertestWithRoleScopeType; - - async function createSpace(id: string) { - await supertestAdminWithApiKey - .post('/api/spaces/space') - .send({ - id, - name: id, - disabledFeatures: [], - }) - .expect(200); - } - - async function deleteSpace(id: string) { - await supertestAdminWithApiKey.delete(`/api/spaces/space/${id}`).expect(204); - } - - describe('spaces', function () { - before(async () => { - supertestAdminWithApiKey = await roleScopedSupertest.getSupertestWithRoleScope('admin', { - withCommonHeaders: true, - }); - supertestAdminWithCookieCredentials = await roleScopedSupertest.getSupertestWithRoleScope( - 'admin', - { - useCookieHeader: true, - withInternalHeaders: true, - } - ); - }); - after(async () => { - // delete any lingering spaces - const { body } = await supertestAdminWithApiKey.get('/api/spaces/space').send().expect(200); - - const toDelete = (body as Array<{ id: string }>).filter((f) => f.id !== 'default'); - - await asyncForEach(toDelete, async (space) => { - await deleteSpace(space.id); - }); - - await supertestAdminWithApiKey.destroy(); - }); - - describe('Create (POST /api/spaces/space)', () => { - it('should allow us to create a space', async () => { - await supertestAdminWithApiKey - .post('/api/spaces/space') - .send({ - id: 'custom_space_1', - name: 'custom_space_1', - disabledFeatures: [], - }) - .expect(200); - }); - - it('should not allow us to create a space with disabled features', async () => { - await supertestAdminWithApiKey - .post('/api/spaces/space') - .send({ - id: 'custom_space_2', - name: 'custom_space_2', - disabledFeatures: ['discover'], - }) - .expect(400); - }); - }); - - describe('Read (GET /api/spaces/space)', () => { - before(async () => { - await createSpace('space_to_get_1'); - await createSpace('space_to_get_2'); - await createSpace('space_to_get_3'); - }); - - after(async () => { - await deleteSpace('space_to_get_1'); - await deleteSpace('space_to_get_2'); - await deleteSpace('space_to_get_3'); - }); - - it('should allow us to get a space', async () => { - await supertestAdminWithApiKey.get('/api/spaces/space/space_to_get_1').send().expect(200, { - id: 'space_to_get_1', - name: 'space_to_get_1', - disabledFeatures: [], - }); - }); - - it('should allow us to get all spaces', async () => { - const { body } = await supertestAdminWithApiKey.get('/api/spaces/space').send().expect(200); - - expect(body).toEqual( - expect.arrayContaining([ - { - _reserved: true, - color: '#00bfb3', - description: 'This is your default space!', - disabledFeatures: [], - id: 'default', - name: 'Default', - }, - { id: 'space_to_get_1', name: 'space_to_get_1', disabledFeatures: [] }, - { id: 'space_to_get_2', name: 'space_to_get_2', disabledFeatures: [] }, - { id: 'space_to_get_3', name: 'space_to_get_3', disabledFeatures: [] }, - ]) - ); - }); - }); - - describe('Update (PUT /api/spaces/space)', () => { - before(async () => { - await createSpace('space_to_update'); - }); - - after(async () => { - await deleteSpace('space_to_update'); - }); - - it('should allow us to update a space', async () => { - await supertestAdminWithApiKey - .put('/api/spaces/space/space_to_update') - .send({ - id: 'space_to_update', - name: 'some new name', - initials: 'SN', - disabledFeatures: [], - }) - .expect(200); - - await supertestAdminWithApiKey.get('/api/spaces/space/space_to_update').send().expect(200, { - id: 'space_to_update', - name: 'some new name', - initials: 'SN', - disabledFeatures: [], - }); - }); - - it('should not allow us to update a space with disabled features', async () => { - await supertestAdminWithApiKey - .put('/api/spaces/space/space_to_update') - .send({ - id: 'space_to_update', - name: 'some new name', - initials: 'SN', - disabledFeatures: ['discover'], - }) - .expect(400); - }); - }); - - describe('Delete (DELETE /api/spaces/space)', () => { - it('should allow us to delete a space', async () => { - await createSpace('space_to_delete'); - - await supertestAdminWithApiKey.delete(`/api/spaces/space/space_to_delete`).expect(204); - }); - }); - - describe('Get active space (GET /internal/spaces/_active_space)', () => { - before(async () => { - await createSpace('foo-space'); - }); - - after(async () => { - await deleteSpace('foo-space'); - }); - - it('returns the default space', async () => { - const response = await supertestAdminWithCookieCredentials - .get('/internal/spaces/_active_space') - .expect(200); - - const { id, name, _reserved } = response.body; - expect({ id, name, _reserved }).toEqual({ - id: 'default', - name: 'Default', - _reserved: true, - }); - }); - - it('returns the default space when explicitly referenced', async () => { - const response = await supertestAdminWithCookieCredentials - .get('/s/default/internal/spaces/_active_space') - .expect(200); - - const { id, name, _reserved } = response.body; - expect({ id, name, _reserved }).toEqual({ - id: 'default', - name: 'Default', - _reserved: true, - }); - }); - - it('returns the foo space', async () => { - await supertestAdminWithCookieCredentials - .get('/s/foo-space/internal/spaces/_active_space') - .expect(200, { - id: 'foo-space', - name: 'foo-space', - disabledFeatures: [], - }); - }); - - it('returns 404 when the space is not found', async () => { - await supertestAdminWithCookieCredentials - .get('/s/not-found-space/internal/spaces/_active_space') - .expect(404, { - statusCode: 404, - error: 'Not Found', - message: 'Saved object [space/not-found-space] not found', - }); - }); - }); - - // These tests just test access to API endpoints, in this case - // when accessed without internal headers they will return 400 - // They will be included in deployment agnostic testing once spaces - // are enabled in production. - describe(`Access`, () => { - describe(`internal`, () => { - it('#getActiveSpace requires internal header', async () => { - let body: any; - let status: number; - - ({ body, status } = await supertestAdminWithApiKey - .get('/internal/spaces/_active_space') - .set(samlAuth.getCommonRequestHeader())); - // expect a rejection because we're not using the internal header - expect(body).toEqual({ - statusCode: 400, - error: 'Bad Request', - message: expect.stringContaining( - 'method [get] exists but is not available with the current configuration' - ), - }); - expect(status).toBe(400); - - ({ body, status } = await supertestAdminWithApiKey - .get('/internal/spaces/_active_space') - .set(samlAuth.getInternalRequestHeader())); - // expect success because we're using the internal header - expect(body).toEqual( - expect.objectContaining({ - id: 'default', - }) - ); - expect(status).toBe(200); - }); - - it('#copyToSpace requires internal header', async () => { - let body: any; - let status: number; - - ({ body, status } = await supertestAdminWithApiKey.post( - '/api/spaces/_copy_saved_objects' - )); - // expect a rejection because we're not using the internal header - expect(body).toEqual({ - statusCode: 400, - error: 'Bad Request', - message: expect.stringContaining( - 'method [post] exists but is not available with the current configuration' - ), - }); - - ({ body, status } = await supertestAdminWithApiKey - .post('/api/spaces/_copy_saved_objects') - .set(samlAuth.getInternalRequestHeader())); - - svlCommonApi.assertResponseStatusCode(400, status, body); - - // expect 400 for missing body - expect(body).toEqual({ - statusCode: 400, - error: 'Bad Request', - message: '[request body]: expected a plain object value, but found [null] instead.', - }); - }); - - it('#resolveCopyToSpaceErrors requires internal header', async () => { - let body: any; - let status: number; - - ({ body, status } = await supertestAdminWithApiKey.post( - '/api/spaces/_resolve_copy_saved_objects_errors' - )); - // expect a rejection because we're not using the internal header - expect(body).toEqual({ - statusCode: 400, - error: 'Bad Request', - message: expect.stringContaining( - 'method [post] exists but is not available with the current configuration' - ), - }); - - ({ body, status } = await supertestAdminWithApiKey - .post('/api/spaces/_resolve_copy_saved_objects_errors') - .set(samlAuth.getInternalRequestHeader())); - - svlCommonApi.assertResponseStatusCode(400, status, body); - - // expect 400 for missing body - expect(body).toEqual({ - statusCode: 400, - error: 'Bad Request', - message: '[request body]: expected a plain object value, but found [null] instead.', - }); - }); - - it('#updateObjectsSpaces requires internal header', async () => { - let body: any; - let status: number; - - ({ body, status } = await supertestAdminWithApiKey.post( - '/api/spaces/_update_objects_spaces' - )); - // expect a rejection because we're not using the internal header - expect(body).toEqual({ - statusCode: 400, - error: 'Bad Request', - message: expect.stringContaining( - 'method [post] exists but is not available with the current configuration' - ), - }); - - ({ body, status } = await supertestAdminWithApiKey - .post('/api/spaces/_update_objects_spaces') - .set(samlAuth.getInternalRequestHeader())); - - svlCommonApi.assertResponseStatusCode(400, status, body); - - // expect 400 for missing body - expect(body).toEqual({ - statusCode: 400, - error: 'Bad Request', - message: '[request body]: expected a plain object value, but found [null] instead.', - }); - }); - - it('#getShareableReferences requires internal header', async () => { - let body: any; - let status: number; - - ({ body, status } = await supertestAdminWithApiKey.post( - '/api/spaces/_get_shareable_references' - )); - // expect a rejection because we're not using the internal header - expect(body).toEqual({ - statusCode: 400, - error: 'Bad Request', - message: expect.stringContaining( - 'method [post] exists but is not available with the current configuration' - ), - }); - - ({ body, status } = await supertestAdminWithApiKey - .post('/api/spaces/_get_shareable_references') - .set(samlAuth.getInternalRequestHeader()) - .send({ - objects: [{ type: 'a', id: 'a' }], - })); - - svlCommonApi.assertResponseStatusCode(200, status, body); - }); - }); - - describe(`disabled`, () => { - it('#disableLegacyUrlAliases', async () => { - const { body, status } = await supertestAdminWithApiKey.post( - '/api/spaces/_disable_legacy_url_aliases' - ); - // without a request body we would normally a 400 bad request if the endpoint was registered - svlCommonApi.assertApiNotFound(body, status); - }); - }); - }); - }); -} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/management/spaces.ts b/x-pack/test_serverless/api_integration/test_suites/common/management/spaces.ts index 4bde897e714af..000848fd37e5f 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/management/spaces.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/management/spaces.ts @@ -7,6 +7,7 @@ import expect from 'expect'; import { SupertestWithRoleScopeType } from '@kbn/test-suites-xpack/api_integration/deployment_agnostic/services'; +import { asyncForEach } from '@kbn/std'; import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { @@ -16,6 +17,21 @@ export default function ({ getService }: FtrProviderContext) { let supertestAdminWithApiKey: SupertestWithRoleScopeType; let supertestAdminWithCookieCredentials: SupertestWithRoleScopeType; + async function createSpace(id: string) { + await supertestAdminWithApiKey + .post('/api/spaces/space') + .send({ + id, + name: id, + disabledFeatures: [], + }) + .expect(200); + } + + async function deleteSpace(id: string) { + await supertestAdminWithApiKey.delete(`/api/spaces/space/${id}`).expect(204); + } + describe('spaces', function () { before(async () => { // admin is the only predefined role that will work for all 3 solutions @@ -34,76 +50,212 @@ export default function ({ getService }: FtrProviderContext) { await supertestAdminWithApiKey.destroy(); }); - describe('route access', () => { - describe('public (CRUD)', () => { - // Skipped due to change in QA environment for role management and spaces - // TODO: revisit once the change is rolled out to all environments - it.skip('#create', async () => { - const { body, status } = await supertestAdminWithApiKey.post('/api/spaces/space').send({ - id: 'custom', - name: 'Custom', - disabledFeatures: [], - }); + // The create and update test cases are unique to serverless because + // setting feature visibility is not possible in serverless + describe('CRUD', () => { + after(async () => { + // delete any lingering spaces + const { body } = await supertestAdminWithApiKey.get('/api/spaces/space').send().expect(200); - svlCommonApi.assertResponseStatusCode(400, status, body); + const toDelete = (body as Array<{ id: string }>).filter((f) => f.id !== 'default'); - // Should fail due to maximum spaces limit, not because of lacking internal header - expect(body).toEqual({ - statusCode: 400, - error: 'Bad Request', - message: - 'Unable to create Space, this exceeds the maximum number of spaces set by the xpack.spaces.maxSpaces setting', - }); + await asyncForEach(toDelete, async (space) => { + await deleteSpace(space.id); }); + }); - it('#get', async () => { - const { body, status } = await supertestAdminWithApiKey.get('/api/spaces/space/default'); - // expect success because we're using the internal header - expect(body).toEqual(expect.objectContaining({ id: 'default' })); - expect(status).toBe(200); + describe('Create (POST /api/spaces/space)', () => { + it('should allow us to create a space', async () => { + await supertestAdminWithApiKey + .post('/api/spaces/space') + .send({ + id: 'custom_space_1', + name: 'custom_space_1', + disabledFeatures: [], + }) + .expect(200); }); - it('#getAll', async () => { - const { body, status } = await supertestAdminWithApiKey.get('/api/spaces/space'); - // expect success because we're using the internal header + it('should not allow us to create a space with disabled features', async () => { + await supertestAdminWithApiKey + .post('/api/spaces/space') + .send({ + id: 'custom_space_2', + name: 'custom_space_2', + disabledFeatures: ['discover'], + }) + .expect(400); + }); + }); + + describe('Read (GET /api/spaces/space)', () => { + before(async () => { + await createSpace('space_to_get_1'); + await createSpace('space_to_get_2'); + await createSpace('space_to_get_3'); + }); + + after(async () => { + await deleteSpace('space_to_get_1'); + await deleteSpace('space_to_get_2'); + await deleteSpace('space_to_get_3'); + }); + + it('should allow us to get a space', async () => { + await supertestAdminWithApiKey + .get('/api/spaces/space/space_to_get_1') + .send() + .expect(200, { + id: 'space_to_get_1', + name: 'space_to_get_1', + disabledFeatures: [], + }); + }); + + it('should allow us to get all spaces', async () => { + const { body } = await supertestAdminWithApiKey + .get('/api/spaces/space') + .send() + .expect(200); + expect(body).toEqual( expect.arrayContaining([ - expect.objectContaining({ + { + _reserved: true, + color: '#00bfb3', + description: 'This is your default space!', + disabledFeatures: [], id: 'default', - }), + name: 'Default', + }, + { id: 'space_to_get_1', name: 'space_to_get_1', disabledFeatures: [] }, + { id: 'space_to_get_2', name: 'space_to_get_2', disabledFeatures: [] }, + { id: 'space_to_get_3', name: 'space_to_get_3', disabledFeatures: [] }, ]) ); - expect(status).toBe(200); }); + }); - it('#update', async () => { - const { body, status } = await supertestAdminWithApiKey - .put('/api/spaces/space/default') + describe('Update (PUT /api/spaces/space)', () => { + before(async () => { + await createSpace('space_to_update'); + }); + + after(async () => { + await deleteSpace('space_to_update'); + }); + + it('should allow us to update a space', async () => { + await supertestAdminWithApiKey + .put('/api/spaces/space/space_to_update') .send({ - id: 'default', - name: 'UPDATED!', + id: 'space_to_update', + name: 'some new name', + initials: 'SN', + disabledFeatures: [], + }) + .expect(200); + + await supertestAdminWithApiKey + .get('/api/spaces/space/space_to_update') + .send() + .expect(200, { + id: 'space_to_update', + name: 'some new name', + initials: 'SN', disabledFeatures: [], }); + }); - svlCommonApi.assertResponseStatusCode(200, status, body); + it('should not allow us to update a space with disabled features', async () => { + await supertestAdminWithApiKey + .put('/api/spaces/space/space_to_update') + .send({ + id: 'space_to_update', + name: 'some new name', + initials: 'SN', + disabledFeatures: ['discover'], + }) + .expect(400); }); + }); - it('#delete', async () => { - const { body, status } = await supertestAdminWithApiKey.delete( - '/api/spaces/space/default' - ); + describe('Delete (DELETE /api/spaces/space)', () => { + it('should allow us to delete a space', async () => { + await createSpace('space_to_delete'); - svlCommonApi.assertResponseStatusCode(400, status, body); + await supertestAdminWithApiKey.delete(`/api/spaces/space/space_to_delete`).expect(204); + }); + }); - // 400 with specific reason - cannot delete the default space - expect(body).toEqual({ - statusCode: 400, - error: 'Bad Request', - message: 'The default space cannot be deleted because it is reserved.', + describe('Get active space (GET /internal/spaces/_active_space)', () => { + before(async () => { + await createSpace('foo-space'); + }); + + after(async () => { + await deleteSpace('foo-space'); + }); + + it('returns the default space', async () => { + const response = await supertestAdminWithCookieCredentials + .get('/internal/spaces/_active_space') + .set(samlAuth.getInternalRequestHeader()) + .expect(200); + + const { id, name, _reserved } = response.body; + expect({ id, name, _reserved }).toEqual({ + id: 'default', + name: 'Default', + _reserved: true, }); }); + + it('returns the default space when explicitly referenced', async () => { + const response = await supertestAdminWithCookieCredentials + .get('/s/default/internal/spaces/_active_space') + .set(samlAuth.getInternalRequestHeader()) + .expect(200); + + const { id, name, _reserved } = response.body; + expect({ id, name, _reserved }).toEqual({ + id: 'default', + name: 'Default', + _reserved: true, + }); + }); + + it('returns the foo space', async () => { + await supertestAdminWithCookieCredentials + .get('/s/foo-space/internal/spaces/_active_space') + .set(samlAuth.getInternalRequestHeader()) + .expect(200, { + id: 'foo-space', + name: 'foo-space', + disabledFeatures: [], + }); + }); + + it('returns 404 when the space is not found', async () => { + await supertestAdminWithCookieCredentials + .get('/s/not-found-space/internal/spaces/_active_space') + .set(samlAuth.getInternalRequestHeader()) + .expect(404, { + statusCode: 404, + error: 'Not Found', + message: 'Saved object [space/not-found-space] not found', + }); + }); }); + }); + describe('route access', () => { + // The 'internal route access' tests check that the internal header + // is needed for these specific endpoints. + // When accessed without internal headers they will return 400. + // They could be moved to deployment agnostic testing if there is + // a way to specify which tests to run when stateful vs serverles, + // as internal vs disabled is different in serverless. describe('internal', () => { it('#getActiveSpace requires internal header', async () => { let body: any; @@ -251,6 +403,7 @@ export default function ({ getService }: FtrProviderContext) { }); }); + // Disabled in serverless, but public in stateful describe('disabled', () => { it('#disableLegacyUrlAliases', async () => { const { body, status } = await supertestAdminWithApiKey @@ -262,26 +415,5 @@ export default function ({ getService }: FtrProviderContext) { }); }); }); - - // TODO: Re-enable test-suite once users can create and update spaces in the Serverless offering. - // it('rejects request to update a space with disabledFeatures', async () => { - // const { body, status } = await supertest - // .put('/api/spaces/space/default') - // .set(svlCommonApi.getInternalRequestHeader()) - // .send({ - // id: 'custom', - // name: 'Custom', - // disabledFeatures: ['some-feature'], - // }); - // - // // in a non-serverless environment this would succeed with a 200 - // expect(body).toEqual({ - // statusCode: 400, - // error: 'Bad Request', - // message: - // 'Unable to update Space, the disabledFeatures array must be empty when xpack.spaces.allowFeatureVisibility setting is disabled', - // }); - // expect(status).toBe(400); - // }); }); } diff --git a/x-pack/test_serverless/api_integration/test_suites/common/platform_security/authorization.ts b/x-pack/test_serverless/api_integration/test_suites/common/platform_security/authorization.ts index fab1a6ef6b265..187efa4e860a8 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/platform_security/authorization.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/platform_security/authorization.ts @@ -8,8 +8,21 @@ import expect from 'expect'; import { KibanaFeatureConfig, SubFeaturePrivilegeConfig } from '@kbn/features-plugin/common'; import { SupertestWithRoleScopeType } from '@kbn/test-suites-xpack/api_integration/deployment_agnostic/services'; +import type { Role } from '@kbn/security-plugin-types-common'; import { FtrProviderContext } from '../../../ftr_provider_context'; +/* + * This file contains authorization tests that... + * - are applicable to all peroject types + * - are applicable to only search and security projects, where custom roles are enabled (role CRUD endpoints are enabled): + * - security/authorization/Roles + * - security/authorization/route access/custom roles + * - are applicable to only observability projects, where custom roles are not enabled (role CRUD endpoints are disabled): + * - security/authorization/route access/disabled/oblt only + * + * The test blocks use skip tags to run only the relevant tests per project type. + */ + function collectSubFeaturesPrivileges(feature: KibanaFeatureConfig) { return new Map( feature.subFeatures?.flatMap((subFeature) => @@ -26,13 +39,12 @@ export default function ({ getService }: FtrProviderContext) { const log = getService('log'); const svlCommonApi = getService('svlCommonApi'); const roleScopedSupertest = getService('roleScopedSupertest'); + const es = getService('es'); let supertestAdminWithCookieCredentials: SupertestWithRoleScopeType; let supertestAdminWithApiKey: SupertestWithRoleScopeType; describe('security/authorization', function () { - // see details: https://github.com/elastic/kibana/issues/192282 - this.tags(['failsOnMKI']); - before(async () => { + before(async function () { supertestAdminWithCookieCredentials = await roleScopedSupertest.getSupertestWithRoleScope( 'admin', { @@ -43,76 +55,1004 @@ export default function ({ getService }: FtrProviderContext) { withCommonHeaders: true, }); }); - after(async () => { + after(async function () { await supertestAdminWithApiKey.destroy(); }); - describe('route access', () => { - // skipped, see https://github.com/elastic/kibana/issues/194933 - describe.skip('disabled', () => { - // Skipped due to change in QA environment for role management and spaces - // TODO: revisit once the change is rolled out to all environments - it.skip('get all privileges', async () => { - const { body, status } = await supertestAdminWithApiKey.get('/api/security/privileges'); - svlCommonApi.assertApiNotFound(body, status); + + describe('Roles', function () { + // custom roles are not enabled for observability projects + this.tags(['skipSvlOblt']); + + describe('Create Role', function () { + it('should allow us to create an empty role', async function () { + await supertestAdminWithApiKey.put('/api/security/role/empty_role').send({}).expect(204); }); - // Skipped due to change in QA environment for role management and spaces - // TODO: revisit once the change is rolled out to all environments - it.skip('get built-in elasticsearch privileges', async () => { - const { body, status } = await supertestAdminWithCookieCredentials.get( - '/internal/security/esPrivileges/builtin' - ); - svlCommonApi.assertApiNotFound(body, status); + it('should create a role with kibana and elasticsearch privileges', async function () { + await supertestAdminWithApiKey + .put('/api/security/role/role_with_privileges') + .send({ + metadata: { + foo: 'test-metadata', + }, + elasticsearch: { + cluster: ['manage'], + indices: [ + { + names: ['logstash-*'], + privileges: ['read', 'view_index_metadata'], + }, + ], + }, + kibana: [ + { + base: ['read'], + }, + { + feature: { + dashboard: ['read'], + discover: ['all'], + ml: ['all'], + }, + spaces: ['marketing', 'sales'], + }, + ], + }) + .expect(204); + + const role = await es.security.getRole({ name: 'role_with_privileges' }); + expect(role).toEqual({ + role_with_privileges: { + cluster: ['manage'], + indices: [ + { + names: ['logstash-*'], + privileges: ['read', 'view_index_metadata'], + allow_restricted_indices: false, + }, + ], + applications: [ + { + application: 'kibana-.kibana', + privileges: ['read'], + resources: ['*'], + }, + { + application: 'kibana-.kibana', + privileges: ['feature_dashboard.read', 'feature_discover.all', 'feature_ml.all'], + resources: ['space:marketing', 'space:sales'], + }, + ], + metadata: { + foo: 'test-metadata', + }, + run_as: [], + transient_metadata: { + enabled: true, + }, + }, + }); }); - // Role CRUD APIs are gated behind the xpack.security.roleManagementEnabled config - // setting. This setting is false by default on serverless. When the custom roles - // feature is enabled, this setting will be true, and the tests from - // roles_routes_feature_flag.ts can be moved here to replace these. - it('create/update roleAuthc', async () => { - const { body, status } = await supertestAdminWithApiKey.put('/api/security/role/test'); - svlCommonApi.assertApiNotFound(body, status); + it(`should create a role with kibana and FLS/DLS elasticsearch privileges`, async function () { + await supertestAdminWithApiKey + .put('/api/security/role/role_with_privileges_dls_fls') + .send({ + metadata: { + foo: 'test-metadata', + }, + elasticsearch: { + cluster: ['manage'], + indices: [ + { + field_security: { + grant: ['*'], + except: ['geo.*'], + }, + names: ['logstash-*'], + privileges: ['read', 'view_index_metadata'], + query: `{ "match": { "geo.src": "CN" } }`, + }, + ], + }, + }) + .expect(204); + }); + + // serverless only (stateful will allow) + it(`should not create a role with 'run as' privileges`, async function () { + await supertestAdminWithApiKey + .put('/api/security/role/role_with_privileges') + .send({ + metadata: { + foo: 'test-metadata', + }, + elasticsearch: { + cluster: ['manage'], + indices: [ + { + names: ['logstash-*'], + privileges: ['read', 'view_index_metadata'], + }, + ], + run_as: ['admin'], + }, + kibana: [ + { + base: ['read'], + }, + { + feature: { + dashboard: ['read'], + discover: ['all'], + ml: ['all'], + }, + spaces: ['marketing', 'sales'], + }, + ], + }) + .expect(400); + }); + + // serverless only (stateful will allow) + it(`should not create a role with remote cluster privileges`, async function () { + await supertestAdminWithApiKey + .put('/api/security/role/role_with_privileges') + .send({ + metadata: { + foo: 'test-metadata', + }, + elasticsearch: { + cluster: ['manage'], + indices: [ + { + names: ['logstash-*'], + privileges: ['read', 'view_index_metadata'], + }, + ], + remote_cluster: [ + { + clusters: ['remote_cluster1'], + privileges: ['monitor_enrich'], + }, + ], + }, + kibana: [ + { + base: ['read'], + }, + { + feature: { + dashboard: ['read'], + discover: ['all'], + ml: ['all'], + }, + spaces: ['marketing', 'sales'], + }, + ], + }) + .expect(400); }); - it('get role', async () => { - const { body, status } = await supertestAdminWithApiKey.get( - '/api/security/role/someRole' // mame of the role doesn't matter, we're checking the endpoint doesn't exist + // serverless only (stateful will allow) + it(`should not create a role with remote index privileges`, async function () { + await supertestAdminWithApiKey + .put('/api/security/role/role_with_privileges') + .send({ + metadata: { + foo: 'test-metadata', + }, + elasticsearch: { + cluster: ['manage'], + indices: [ + { + names: ['logstash-*'], + privileges: ['read', 'view_index_metadata'], + }, + ], + remote_indices: [ + { + clusters: ['remote_cluster1'], + names: ['remote_index1', 'remote_index2'], + privileges: ['all'], + }, + ], + }, + kibana: [ + { + base: ['read'], + }, + { + feature: { + dashboard: ['read'], + discover: ['all'], + ml: ['all'], + }, + spaces: ['marketing', 'sales'], + }, + ], + }) + .expect(400); + }); + + describe('with the createOnly option enabled', function () { + it('should fail when role already exists', async function () { + await es.security.putRole({ + name: 'test_role', + body: { + cluster: ['monitor'], + indices: [ + { + names: ['beats-*'], + privileges: ['write'], + }, + ], + }, + }); + + await supertestAdminWithApiKey + .put('/api/security/role/test_role?createOnly=true') + .send({}) + .expect(409); + }); + + it('should succeed when role does not exist', async function () { + await supertestAdminWithApiKey + .put('/api/security/role/new_role?createOnly=true') + .send({}) + .expect(204); + }); + }); + }); + + describe('Read Role', function () { + it('should get roles', async function () { + await es.security.putRole({ + name: 'role_to_get', + body: { + cluster: ['manage'], + indices: [ + { + names: ['logstash-*'], + privileges: ['read', 'view_index_metadata'], + }, + ], + applications: [ + { + application: 'kibana-.kibana', + privileges: ['read'], + resources: ['*'], + }, + { + application: 'kibana-.kibana', + privileges: ['feature_dashboard.read', 'feature_discover.all', 'feature_ml.all'], + resources: ['space:marketing', 'space:sales'], + }, + { + application: 'apm', + privileges: ['apm-privilege'], + resources: ['*'], + }, + ], + metadata: { + foo: 'test-metadata', + }, + transient_metadata: { + enabled: true, + }, + }, + }); + + await supertestAdminWithApiKey.get('/api/security/role/role_to_get').expect(200, { + name: 'role_to_get', + metadata: { + foo: 'test-metadata', + }, + transient_metadata: { enabled: true }, + elasticsearch: { + cluster: ['manage'], + indices: [ + { + names: ['logstash-*'], + privileges: ['read', 'view_index_metadata'], + allow_restricted_indices: false, + }, + ], + run_as: [], + }, + kibana: [ + { + base: ['read'], + feature: {}, + spaces: ['*'], + }, + { + base: [], + feature: { + dashboard: ['read'], + discover: ['all'], + ml: ['all'], + }, + spaces: ['marketing', 'sales'], + }, + ], + + _transform_error: [], + _unrecognized_applications: ['apm'], + }); + }); + + it('should get roles by space id', async function () { + await es.security.putRole({ + name: 'space_role_not_to_get', + body: { + cluster: ['manage'], + indices: [ + { + names: ['logstash-*'], + privileges: ['read', 'view_index_metadata'], + }, + ], + applications: [ + { + application: 'kibana-.kibana', + privileges: ['feature_dashboard.read', 'feature_discover.all', 'feature_ml.all'], + resources: ['space:marketing', 'space:sales'], + }, + ], + metadata: { + foo: 'test-metadata', + }, + transient_metadata: { + enabled: true, + }, + }, + }); + + await es.security.putRole({ + name: 'space_role_to_get', + body: { + cluster: ['manage'], + indices: [ + { + names: ['logstash-*'], + privileges: ['read', 'view_index_metadata'], + }, + ], + applications: [ + { + application: 'kibana-.kibana', + privileges: ['feature_dashboard.read', 'feature_discover.all', 'feature_ml.all'], + resources: ['space:engineering', 'space:sales'], + }, + ], + metadata: { + foo: 'test-metadata', + }, + transient_metadata: { + enabled: true, + }, + }, + }); + + await supertestAdminWithCookieCredentials + .get('/internal/security/roles/engineering') + .set(svlCommonApi.getInternalRequestHeader()) + .expect(200) + .expect((res: { body: Role[] }) => { + const roles = res.body; + + const success = roles.every((role) => { + return ( + role.name !== 'space_role_not_to_get' && + role.kibana.some((privilege) => { + return ( + privilege.spaces.includes('*') || privilege.spaces.includes('engineering') + ); + }) + ); + }); + + const expectedRole = roles.find((role) => role.name === 'space_role_to_get'); + + expect(success).toBe(true); + expect(expectedRole).toBeTruthy(); + }); + }); + }); + + describe('Update Role', function () { + it('should update a role with elasticsearch, kibana and other applications privileges', async function () { + await es.security.putRole({ + name: 'role_to_update', + body: { + cluster: ['monitor'], + indices: [ + { + names: ['beats-*'], + privileges: ['write'], + }, + ], + applications: [ + { + application: 'kibana-.kibana', + privileges: ['read'], + resources: ['*'], + }, + { + application: 'apm', + privileges: ['apm-privilege'], + resources: ['*'], + }, + ], + metadata: { + bar: 'old-metadata', + }, + }, + }); + + await supertestAdminWithApiKey + .put('/api/security/role/role_to_update') + .send({ + metadata: { + foo: 'test-metadata', + }, + elasticsearch: { + cluster: ['manage'], + indices: [ + { + names: ['logstash-*'], + privileges: ['read', 'view_index_metadata'], + }, + ], + }, + kibana: [ + { + feature: { + dashboard: ['read'], + dev_tools: ['all'], + }, + spaces: ['*'], + }, + { + base: ['all'], + spaces: ['marketing', 'sales'], + }, + ], + }) + .expect(204); + + const role = await es.security.getRole({ name: 'role_to_update' }); + expect(role).toEqual({ + role_to_update: { + cluster: ['manage'], + indices: [ + { + names: ['logstash-*'], + privileges: ['read', 'view_index_metadata'], + allow_restricted_indices: false, + }, + ], + applications: [ + { + application: 'kibana-.kibana', + privileges: ['feature_dashboard.read', 'feature_dev_tools.all'], + resources: ['*'], + }, + { + application: 'kibana-.kibana', + privileges: ['space_all'], + resources: ['space:marketing', 'space:sales'], + }, + { + application: 'apm', + privileges: ['apm-privilege'], + resources: ['*'], + }, + ], + metadata: { + foo: 'test-metadata', + }, + run_as: [], + transient_metadata: { + enabled: true, + }, + }, + }); + }); + + it(`should update a role adding DLS and FLS privileges`, async function () { + await es.security.putRole({ + name: 'role_to_update_with_dls_fls', + body: { + cluster: ['monitor'], + indices: [ + { + names: ['beats-*'], + privileges: ['write'], + }, + ], + }, + }); + + await supertestAdminWithApiKey + .put('/api/security/role/role_to_update_with_dls_fls') + .send({ + elasticsearch: { + cluster: ['manage'], + indices: [ + { + field_security: { + grant: ['*'], + except: ['geo.*'], + }, + names: ['logstash-*'], + privileges: ['read'], + query: `{ "match": { "geo.src": "CN" } }`, + }, + ], + }, + }) + .expect(204); + + const role = await es.security.getRole({ name: 'role_to_update_with_dls_fls' }); + + expect(role.role_to_update_with_dls_fls.cluster).toEqual(['manage']); + expect(role.role_to_update_with_dls_fls.indices[0].names).toEqual(['logstash-*']); + expect(role.role_to_update_with_dls_fls.indices[0].query).toEqual( + `{ "match": { "geo.src": "CN" } }` ); - svlCommonApi.assertApiNotFound(body, status); }); - it('get all roles', async () => { - const { body, status } = await supertestAdminWithApiKey.get('/api/security/role'); - svlCommonApi.assertApiNotFound(body, status); + // serverless only (stateful will allow) + it(`should not update a role with 'run as' privileges`, async function () { + await es.security.putRole({ + name: 'role_to_update', + body: { + cluster: ['monitor'], + indices: [ + { + names: ['beats-*'], + privileges: ['write'], + }, + ], + applications: [ + { + application: 'kibana-.kibana', + privileges: ['read'], + resources: ['*'], + }, + { + application: 'apm', + privileges: ['apm-privilege'], + resources: ['*'], + }, + ], + metadata: { + bar: 'old-metadata', + }, + }, + }); + + await supertestAdminWithApiKey + .put('/api/security/role/role_to_update') + .send({ + metadata: { + foo: 'test-metadata', + }, + elasticsearch: { + cluster: ['manage'], + indices: [ + { + names: ['logstash-*'], + privileges: ['read', 'view_index_metadata'], + }, + ], + run_as: ['admin'], + }, + kibana: [ + { + feature: { + dashboard: ['read'], + dev_tools: ['all'], + }, + spaces: ['*'], + }, + { + base: ['all'], + spaces: ['marketing', 'sales'], + }, + ], + }) + .expect(400); + + const role = await es.security.getRole({ name: 'role_to_update' }); + expect(role).toEqual({ + role_to_update: { + cluster: ['monitor'], + indices: [ + { + names: ['beats-*'], + privileges: ['write'], + allow_restricted_indices: false, + }, + ], + applications: [ + { + application: 'kibana-.kibana', + privileges: ['read'], + resources: ['*'], + }, + { + application: 'apm', + privileges: ['apm-privilege'], + resources: ['*'], + }, + ], + metadata: { + bar: 'old-metadata', + }, + run_as: [], + transient_metadata: { + enabled: true, + }, + }, + }); + }); + + // serverless only (stateful will allow) + it(`should not update a role with remote cluster privileges`, async function () { + await es.security.putRole({ + name: 'role_to_update', + body: { + cluster: ['monitor'], + indices: [ + { + names: ['beats-*'], + privileges: ['write'], + }, + ], + applications: [ + { + application: 'kibana-.kibana', + privileges: ['read'], + resources: ['*'], + }, + { + application: 'apm', + privileges: ['apm-privilege'], + resources: ['*'], + }, + ], + metadata: { + bar: 'old-metadata', + }, + }, + }); + + await supertestAdminWithApiKey + .put('/api/security/role/role_to_update') + .send({ + metadata: { + foo: 'test-metadata', + }, + elasticsearch: { + cluster: ['manage'], + indices: [ + { + names: ['logstash-*'], + privileges: ['read', 'view_index_metadata'], + }, + ], + remote_cluster: [ + { + clusters: ['remote_cluster1'], + privileges: ['monitor_enrich'], + }, + ], + }, + kibana: [ + { + feature: { + dashboard: ['read'], + dev_tools: ['all'], + }, + spaces: ['*'], + }, + { + base: ['all'], + spaces: ['marketing', 'sales'], + }, + ], + }) + .expect(400); + + const role = await es.security.getRole({ name: 'role_to_update' }); + expect(role).toEqual({ + role_to_update: { + cluster: ['monitor'], + indices: [ + { + names: ['beats-*'], + privileges: ['write'], + allow_restricted_indices: false, + }, + ], + applications: [ + { + application: 'kibana-.kibana', + privileges: ['read'], + resources: ['*'], + }, + { + application: 'apm', + privileges: ['apm-privilege'], + resources: ['*'], + }, + ], + metadata: { + bar: 'old-metadata', + }, + run_as: [], + transient_metadata: { + enabled: true, + }, + }, + }); }); - it('delete role', async () => { - const { body, status } = await supertestAdminWithApiKey.delete( - '/api/security/role/someRole' // mame of the role doesn't matter, we're checking the endpoint doesn't exist + // serverless only (stateful will allow) + it(`should not update a role with remote index privileges`, async function () { + await es.security.putRole({ + name: 'role_to_update', + body: { + cluster: ['monitor'], + indices: [ + { + names: ['beats-*'], + privileges: ['write'], + }, + ], + applications: [ + { + application: 'kibana-.kibana', + privileges: ['read'], + resources: ['*'], + }, + { + application: 'apm', + privileges: ['apm-privilege'], + resources: ['*'], + }, + ], + metadata: { + bar: 'old-metadata', + }, + }, + }); + + await supertestAdminWithApiKey + .put('/api/security/role/role_to_update') + .send({ + metadata: { + foo: 'test-metadata', + }, + elasticsearch: { + cluster: ['manage'], + indices: [ + { + names: ['logstash-*'], + privileges: ['read', 'view_index_metadata'], + }, + ], + remote_indices: [ + { + clusters: ['remote_cluster1'], + names: ['remote_index1', 'remote_index2'], + privileges: ['all'], + }, + ], + }, + kibana: [ + { + feature: { + dashboard: ['read'], + dev_tools: ['all'], + }, + spaces: ['*'], + }, + { + base: ['all'], + spaces: ['marketing', 'sales'], + }, + ], + }) + .expect(400); + + const role = await es.security.getRole({ name: 'role_to_update' }); + expect(role).toEqual({ + role_to_update: { + cluster: ['monitor'], + indices: [ + { + names: ['beats-*'], + privileges: ['write'], + allow_restricted_indices: false, + }, + ], + applications: [ + { + application: 'kibana-.kibana', + privileges: ['read'], + resources: ['*'], + }, + { + application: 'apm', + privileges: ['apm-privilege'], + resources: ['*'], + }, + ], + metadata: { + bar: 'old-metadata', + }, + run_as: [], + transient_metadata: { + enabled: true, + }, + }, + }); + }); + }); + + describe('Delete Role', function () { + it('should delete an existing role', async function () { + await es.security.putRole({ + name: 'role_to_delete', + body: { + cluster: ['monitor'], + indices: [ + { + names: ['beats-*'], + privileges: ['write'], + }, + ], + applications: [ + { + application: 'kibana-.kibana', + privileges: ['read'], + resources: ['*'], + }, + { + application: 'apm', + privileges: ['apm-privilege'], + resources: ['*'], + }, + ], + metadata: { + bar: 'old-metadata', + }, + }, + }); + await supertestAdminWithApiKey.delete('/api/security/role/role_to_delete').expect(204); + + const deletedRole = await es.security.getRole( + { name: 'role_to_delete' }, + { ignore: [404] } ); - svlCommonApi.assertApiNotFound(body, status); + expect(deletedRole).toEqual({}); }); + }); + }); - it('get shared saved object permissions', async () => { + describe('route access', function () { + describe('disabled', function () { + it('get shared saved object permissions', async function () { const { body, status } = await supertestAdminWithCookieCredentials.get( '/internal/security/_share_saved_object_permissions' ); svlCommonApi.assertApiNotFound(body, status); }); + + describe('oblt only', function () { + // custom roles are not enabled for observability projects + this.tags(['skipSvlSearch', 'skipSvlSec']); + + it('create/update role', async function () { + const { body, status } = await supertestAdminWithApiKey.put('/api/security/role/test'); + svlCommonApi.assertApiNotFound(body, status); + }); + + it('get role', async function () { + const { body, status } = await supertestAdminWithApiKey.get( + '/api/security/role/superuser' + ); + svlCommonApi.assertApiNotFound(body, status); + }); + + it('get all roles', async function () { + const { body, status } = await supertestAdminWithApiKey.get('/api/security/role'); + svlCommonApi.assertApiNotFound(body, status); + }); + + it('delete role', async function () { + const { body, status } = await supertestAdminWithApiKey.delete( + '/api/security/role/superuser' + ); + svlCommonApi.assertApiNotFound(body, status); + }); + + it('get all privileges', async function () { + const { body, status } = await supertestAdminWithApiKey.get('/api/security/privileges'); + svlCommonApi.assertApiNotFound(body, status); + }); + + it('get built-in elasticsearch privileges', async function () { + const { body, status } = await supertestAdminWithCookieCredentials.get( + '/internal/security/esPrivileges/builtin' + ); + svlCommonApi.assertApiNotFound(body, status); + }); + }); }); - describe('public', () => { - it('reset session page', async () => { + describe('public', function () { + // Public but undocumented, hence 'internal' in path + it('reset session page', async function () { const { status } = await supertestAdminWithCookieCredentials.get( '/internal/security/reset_session_page.js' ); expect(status).toBe(200); }); }); + + describe('custom roles', function () { + // custom roles are not enabled for observability projects + this.tags(['skipSvlOblt']); + + describe('internal', function () { + it('get built-in elasticsearch privileges', async function () { + let body: any; + let status: number; + + ({ body, status } = await supertestAdminWithCookieCredentials + .get('/internal/security/esPrivileges/builtin') + .set(svlCommonApi.getCommonRequestHeader())); + // expect a rejection because we're not using the internal header + expect(body).toEqual({ + statusCode: 400, + error: 'Bad Request', + message: expect.stringContaining( + 'method [get] exists but is not available with the current configuration' + ), + }); + expect(status).toBe(400); + + ({ status } = await supertestAdminWithCookieCredentials.get( + '/internal/security/esPrivileges/builtin' + )); + expect(status).toBe(400); + + // expect success when using the internal header + ({ body, status } = await supertestAdminWithCookieCredentials + .get('/internal/security/esPrivileges/builtin') + .set(svlCommonApi.getInternalRequestHeader())); + expect(status).toBe(200); + }); + }); + + describe('public', function () { + it('get all privileges', async function () { + const { status } = await supertestAdminWithApiKey + .get('/api/security/privileges') + .set(svlCommonApi.getInternalRequestHeader()); + expect(status).toBe(200); + }); + }); + }); }); - describe('available features', () => { - it('all Dashboard and Discover sub-feature privileges are disabled', async () => { + describe('available features', function () { + it('all Dashboard and Discover sub-feature privileges are disabled', async function () { const { body } = await supertestAdminWithCookieCredentials.get('/api/features').expect(200); // We should make sure that neither Discover nor Dashboard displays any sub-feature privileges in Serverless. diff --git a/x-pack/test_serverless/api_integration/test_suites/common/platform_security/roles_routes_feature_flag.ts b/x-pack/test_serverless/api_integration/test_suites/common/platform_security/roles_routes_feature_flag.ts deleted file mode 100644 index 7f2237eda4b44..0000000000000 --- a/x-pack/test_serverless/api_integration/test_suites/common/platform_security/roles_routes_feature_flag.ts +++ /dev/null @@ -1,951 +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 expect from 'expect'; -import type { Role } from '@kbn/security-plugin-types-common'; -import { SupertestWithRoleScopeType } from '@kbn/test-suites-xpack/api_integration/deployment_agnostic/services'; -import { FtrProviderContext } from '../../../ftr_provider_context'; - -// Notes: -// Test coverage comes from stateful test suite: x-pack/test/api_integration/apis/security/roles.ts -// It has been modified to work for serverless by removing invalid options (run_as, allow_restricted_indices, etc). -// -// Note: this suite is currently only called from the feature flags test configs, e.g. -// x-pack/test_serverless/api_integration/test_suites/search/config.feature_flags.ts -// -// This suite should be converted into a deployment agnostic suite when the native roles -// feature flags are enabled permanently in serverless. Additionally, the route access tests -// for the roles APIs in authorization.ts should also get updated at that time. -// kbnServerArgs: ['--xpack.security.roleManagementEnabled=true'], -// esServerArgs: ['xpack.security.authc.native_roles.enabled=true'], - -export default function ({ getService }: FtrProviderContext) { - const platformSecurityUtils = getService('platformSecurityUtils'); - const roleScopedSupertest = getService('roleScopedSupertest'); - const svlCommonApi = getService('svlCommonApi'); - let supertestAdminWithApiKey: SupertestWithRoleScopeType; - let supertestAdminWithCookieCredentials: SupertestWithRoleScopeType; - const es = getService('es'); - - describe('security', function () { - describe('Roles', () => { - before(async () => { - supertestAdminWithCookieCredentials = await roleScopedSupertest.getSupertestWithRoleScope( - 'admin', - { - useCookieHeader: true, - withInternalHeaders: true, - } - ); - supertestAdminWithApiKey = await roleScopedSupertest.getSupertestWithRoleScope('admin', { - withCommonHeaders: true, - }); - }); - after(async () => { - await platformSecurityUtils.clearAllRoles(); - }); - - describe('Create Role', () => { - it('should allow us to create an empty role', async () => { - await supertestAdminWithApiKey.put('/api/security/role/empty_role').send({}).expect(204); - }); - - it('should create a role with kibana and elasticsearch privileges', async () => { - await supertestAdminWithApiKey - .put('/api/security/role/role_with_privileges') - .send({ - metadata: { - foo: 'test-metadata', - }, - elasticsearch: { - cluster: ['manage'], - indices: [ - { - names: ['logstash-*'], - privileges: ['read', 'view_index_metadata'], - }, - ], - }, - kibana: [ - { - base: ['read'], - }, - { - feature: { - dashboard: ['read'], - discover: ['all'], - ml: ['all'], - }, - spaces: ['marketing', 'sales'], - }, - ], - }) - .expect(204); - - const role = await es.security.getRole({ name: 'role_with_privileges' }); - expect(role).toEqual({ - role_with_privileges: { - cluster: ['manage'], - indices: [ - { - names: ['logstash-*'], - privileges: ['read', 'view_index_metadata'], - allow_restricted_indices: false, - }, - ], - applications: [ - { - application: 'kibana-.kibana', - privileges: ['read'], - resources: ['*'], - }, - { - application: 'kibana-.kibana', - privileges: ['feature_dashboard.read', 'feature_discover.all', 'feature_ml.all'], - resources: ['space:marketing', 'space:sales'], - }, - ], - metadata: { - foo: 'test-metadata', - }, - run_as: [], - transient_metadata: { - enabled: true, - }, - }, - }); - }); - - it(`should create a role with kibana and FLS/DLS elasticsearch privileges`, async () => { - await supertestAdminWithApiKey - .put('/api/security/role/role_with_privileges_dls_fls') - .send({ - metadata: { - foo: 'test-metadata', - }, - elasticsearch: { - cluster: ['manage'], - indices: [ - { - field_security: { - grant: ['*'], - except: ['geo.*'], - }, - names: ['logstash-*'], - privileges: ['read', 'view_index_metadata'], - query: `{ "match": { "geo.src": "CN" } }`, - }, - ], - }, - }) - .expect(204); - }); - - // serverless only (stateful will allow) - it(`should not create a role with 'run as' privileges`, async () => { - await supertestAdminWithApiKey - .put('/api/security/role/role_with_privileges') - .send({ - metadata: { - foo: 'test-metadata', - }, - elasticsearch: { - cluster: ['manage'], - indices: [ - { - names: ['logstash-*'], - privileges: ['read', 'view_index_metadata'], - }, - ], - run_as: ['admin'], - }, - kibana: [ - { - base: ['read'], - }, - { - feature: { - dashboard: ['read'], - discover: ['all'], - ml: ['all'], - }, - spaces: ['marketing', 'sales'], - }, - ], - }) - .expect(400); - }); - - // serverless only (stateful will allow) - it(`should not create a role with remote cluster privileges`, async () => { - await supertestAdminWithApiKey - .put('/api/security/role/role_with_privileges') - .send({ - metadata: { - foo: 'test-metadata', - }, - elasticsearch: { - cluster: ['manage'], - indices: [ - { - names: ['logstash-*'], - privileges: ['read', 'view_index_metadata'], - }, - ], - remote_cluster: [ - { - clusters: ['remote_cluster1'], - privileges: ['monitor_enrich'], - }, - ], - }, - kibana: [ - { - base: ['read'], - }, - { - feature: { - dashboard: ['read'], - discover: ['all'], - ml: ['all'], - }, - spaces: ['marketing', 'sales'], - }, - ], - }) - .expect(400); - }); - - // serverless only (stateful will allow) - it(`should not create a role with remote index privileges`, async () => { - await supertestAdminWithApiKey - .put('/api/security/role/role_with_privileges') - .send({ - metadata: { - foo: 'test-metadata', - }, - elasticsearch: { - cluster: ['manage'], - indices: [ - { - names: ['logstash-*'], - privileges: ['read', 'view_index_metadata'], - }, - ], - remote_indices: [ - { - clusters: ['remote_cluster1'], - names: ['remote_index1', 'remote_index2'], - privileges: ['all'], - }, - ], - }, - kibana: [ - { - base: ['read'], - }, - { - feature: { - dashboard: ['read'], - discover: ['all'], - ml: ['all'], - }, - spaces: ['marketing', 'sales'], - }, - ], - }) - .expect(400); - }); - - describe('with the createOnly option enabled', () => { - it('should fail when role already exists', async () => { - await es.security.putRole({ - name: 'test_role', - body: { - cluster: ['monitor'], - indices: [ - { - names: ['beats-*'], - privileges: ['write'], - }, - ], - }, - }); - - await supertestAdminWithApiKey - .put('/api/security/role/test_role?createOnly=true') - .send({}) - .expect(409); - }); - - it('should succeed when role does not exist', async () => { - await supertestAdminWithApiKey - .put('/api/security/role/new_role?createOnly=true') - .send({}) - .expect(204); - }); - }); - }); - - describe('Read Role', () => { - it('should get roles', async () => { - await es.security.putRole({ - name: 'role_to_get', - body: { - cluster: ['manage'], - indices: [ - { - names: ['logstash-*'], - privileges: ['read', 'view_index_metadata'], - }, - ], - applications: [ - { - application: 'kibana-.kibana', - privileges: ['read'], - resources: ['*'], - }, - { - application: 'kibana-.kibana', - privileges: ['feature_dashboard.read', 'feature_discover.all', 'feature_ml.all'], - resources: ['space:marketing', 'space:sales'], - }, - { - application: 'apm', - privileges: ['apm-privilege'], - resources: ['*'], - }, - ], - metadata: { - foo: 'test-metadata', - }, - transient_metadata: { - enabled: true, - }, - }, - }); - - await supertestAdminWithApiKey.get('/api/security/role/role_to_get').expect(200, { - name: 'role_to_get', - metadata: { - foo: 'test-metadata', - }, - transient_metadata: { enabled: true }, - elasticsearch: { - cluster: ['manage'], - indices: [ - { - names: ['logstash-*'], - privileges: ['read', 'view_index_metadata'], - allow_restricted_indices: false, - }, - ], - run_as: [], - }, - kibana: [ - { - base: ['read'], - feature: {}, - spaces: ['*'], - }, - { - base: [], - feature: { - dashboard: ['read'], - discover: ['all'], - ml: ['all'], - }, - spaces: ['marketing', 'sales'], - }, - ], - - _transform_error: [], - _unrecognized_applications: ['apm'], - }); - }); - - it('should get roles by space id', async () => { - await es.security.putRole({ - name: 'space_role_not_to_get', - body: { - cluster: ['manage'], - indices: [ - { - names: ['logstash-*'], - privileges: ['read', 'view_index_metadata'], - }, - ], - applications: [ - { - application: 'kibana-.kibana', - privileges: ['feature_dashboard.read', 'feature_discover.all', 'feature_ml.all'], - resources: ['space:marketing', 'space:sales'], - }, - ], - metadata: { - foo: 'test-metadata', - }, - transient_metadata: { - enabled: true, - }, - }, - }); - - await es.security.putRole({ - name: 'space_role_to_get', - body: { - cluster: ['manage'], - indices: [ - { - names: ['logstash-*'], - privileges: ['read', 'view_index_metadata'], - }, - ], - applications: [ - { - application: 'kibana-.kibana', - privileges: ['feature_dashboard.read', 'feature_discover.all', 'feature_ml.all'], - resources: ['space:engineering', 'space:sales'], - }, - ], - metadata: { - foo: 'test-metadata', - }, - transient_metadata: { - enabled: true, - }, - }, - }); - - await supertestAdminWithCookieCredentials - .get('/internal/security/roles/engineering') - .expect(200) - .expect((res: { body: Role[] }) => { - const roles = res.body; - - const success = roles.every((role) => { - return ( - role.name !== 'space_role_not_to_get' && - role.kibana.some((privilege) => { - return ( - privilege.spaces.includes('*') || privilege.spaces.includes('engineering') - ); - }) - ); - }); - - const expectedRole = roles.find((role) => role.name === 'space_role_to_get'); - - expect(success).toBe(true); - expect(expectedRole).toBeTruthy(); - }); - }); - }); - - describe('Update Role', () => { - it('should update a role with elasticsearch, kibana and other applications privileges', async () => { - await es.security.putRole({ - name: 'role_to_update', - body: { - cluster: ['monitor'], - indices: [ - { - names: ['beats-*'], - privileges: ['write'], - }, - ], - applications: [ - { - application: 'kibana-.kibana', - privileges: ['read'], - resources: ['*'], - }, - { - application: 'apm', - privileges: ['apm-privilege'], - resources: ['*'], - }, - ], - metadata: { - bar: 'old-metadata', - }, - }, - }); - - await supertestAdminWithApiKey - .put('/api/security/role/role_to_update') - .send({ - metadata: { - foo: 'test-metadata', - }, - elasticsearch: { - cluster: ['manage'], - indices: [ - { - names: ['logstash-*'], - privileges: ['read', 'view_index_metadata'], - }, - ], - }, - kibana: [ - { - feature: { - dashboard: ['read'], - dev_tools: ['all'], - }, - spaces: ['*'], - }, - { - base: ['all'], - spaces: ['marketing', 'sales'], - }, - ], - }) - .expect(204); - - const role = await es.security.getRole({ name: 'role_to_update' }); - expect(role).toEqual({ - role_to_update: { - cluster: ['manage'], - indices: [ - { - names: ['logstash-*'], - privileges: ['read', 'view_index_metadata'], - allow_restricted_indices: false, - }, - ], - applications: [ - { - application: 'kibana-.kibana', - privileges: ['feature_dashboard.read', 'feature_dev_tools.all'], - resources: ['*'], - }, - { - application: 'kibana-.kibana', - privileges: ['space_all'], - resources: ['space:marketing', 'space:sales'], - }, - { - application: 'apm', - privileges: ['apm-privilege'], - resources: ['*'], - }, - ], - metadata: { - foo: 'test-metadata', - }, - run_as: [], - transient_metadata: { - enabled: true, - }, - }, - }); - }); - - it(`should update a role adding DLS and FLS privileges`, async () => { - await es.security.putRole({ - name: 'role_to_update_with_dls_fls', - body: { - cluster: ['monitor'], - indices: [ - { - names: ['beats-*'], - privileges: ['write'], - }, - ], - }, - }); - - await supertestAdminWithApiKey - .put('/api/security/role/role_to_update_with_dls_fls') - .send({ - elasticsearch: { - cluster: ['manage'], - indices: [ - { - field_security: { - grant: ['*'], - except: ['geo.*'], - }, - names: ['logstash-*'], - privileges: ['read'], - query: `{ "match": { "geo.src": "CN" } }`, - }, - ], - }, - }) - .expect(204); - - const role = await es.security.getRole({ name: 'role_to_update_with_dls_fls' }); - - expect(role.role_to_update_with_dls_fls.cluster).toEqual(['manage']); - expect(role.role_to_update_with_dls_fls.indices[0].names).toEqual(['logstash-*']); - expect(role.role_to_update_with_dls_fls.indices[0].query).toEqual( - `{ "match": { "geo.src": "CN" } }` - ); - }); - - // serverless only (stateful will allow) - it(`should not update a role with 'run as' privileges`, async () => { - await es.security.putRole({ - name: 'role_to_update', - body: { - cluster: ['monitor'], - indices: [ - { - names: ['beats-*'], - privileges: ['write'], - }, - ], - applications: [ - { - application: 'kibana-.kibana', - privileges: ['read'], - resources: ['*'], - }, - { - application: 'apm', - privileges: ['apm-privilege'], - resources: ['*'], - }, - ], - metadata: { - bar: 'old-metadata', - }, - }, - }); - - await supertestAdminWithApiKey - .put('/api/security/role/role_to_update') - .send({ - metadata: { - foo: 'test-metadata', - }, - elasticsearch: { - cluster: ['manage'], - indices: [ - { - names: ['logstash-*'], - privileges: ['read', 'view_index_metadata'], - }, - ], - run_as: ['admin'], - }, - kibana: [ - { - feature: { - dashboard: ['read'], - dev_tools: ['all'], - }, - spaces: ['*'], - }, - { - base: ['all'], - spaces: ['marketing', 'sales'], - }, - ], - }) - .expect(400); - - const role = await es.security.getRole({ name: 'role_to_update' }); - expect(role).toEqual({ - role_to_update: { - cluster: ['monitor'], - indices: [ - { - names: ['beats-*'], - privileges: ['write'], - allow_restricted_indices: false, - }, - ], - applications: [ - { - application: 'kibana-.kibana', - privileges: ['read'], - resources: ['*'], - }, - { - application: 'apm', - privileges: ['apm-privilege'], - resources: ['*'], - }, - ], - metadata: { - bar: 'old-metadata', - }, - run_as: [], - transient_metadata: { - enabled: true, - }, - }, - }); - }); - - // serverless only (stateful will allow) - it(`should not update a role with remote cluster privileges`, async () => { - await es.security.putRole({ - name: 'role_to_update', - body: { - cluster: ['monitor'], - indices: [ - { - names: ['beats-*'], - privileges: ['write'], - }, - ], - applications: [ - { - application: 'kibana-.kibana', - privileges: ['read'], - resources: ['*'], - }, - { - application: 'apm', - privileges: ['apm-privilege'], - resources: ['*'], - }, - ], - metadata: { - bar: 'old-metadata', - }, - }, - }); - - await supertestAdminWithApiKey - .put('/api/security/role/role_to_update') - .send({ - metadata: { - foo: 'test-metadata', - }, - elasticsearch: { - cluster: ['manage'], - indices: [ - { - names: ['logstash-*'], - privileges: ['read', 'view_index_metadata'], - }, - ], - remote_cluster: [ - { - clusters: ['remote_cluster1'], - privileges: ['monitor_enrich'], - }, - ], - }, - kibana: [ - { - feature: { - dashboard: ['read'], - dev_tools: ['all'], - }, - spaces: ['*'], - }, - { - base: ['all'], - spaces: ['marketing', 'sales'], - }, - ], - }) - .expect(400); - - const role = await es.security.getRole({ name: 'role_to_update' }); - expect(role).toEqual({ - role_to_update: { - cluster: ['monitor'], - indices: [ - { - names: ['beats-*'], - privileges: ['write'], - allow_restricted_indices: false, - }, - ], - applications: [ - { - application: 'kibana-.kibana', - privileges: ['read'], - resources: ['*'], - }, - { - application: 'apm', - privileges: ['apm-privilege'], - resources: ['*'], - }, - ], - metadata: { - bar: 'old-metadata', - }, - run_as: [], - transient_metadata: { - enabled: true, - }, - }, - }); - }); - - // serverless only (stateful will allow) - it(`should not update a role with remote index privileges`, async () => { - await es.security.putRole({ - name: 'role_to_update', - body: { - cluster: ['monitor'], - indices: [ - { - names: ['beats-*'], - privileges: ['write'], - }, - ], - applications: [ - { - application: 'kibana-.kibana', - privileges: ['read'], - resources: ['*'], - }, - { - application: 'apm', - privileges: ['apm-privilege'], - resources: ['*'], - }, - ], - metadata: { - bar: 'old-metadata', - }, - }, - }); - - await supertestAdminWithApiKey - .put('/api/security/role/role_to_update') - .send({ - metadata: { - foo: 'test-metadata', - }, - elasticsearch: { - cluster: ['manage'], - indices: [ - { - names: ['logstash-*'], - privileges: ['read', 'view_index_metadata'], - }, - ], - remote_indices: [ - { - clusters: ['remote_cluster1'], - names: ['remote_index1', 'remote_index2'], - privileges: ['all'], - }, - ], - }, - kibana: [ - { - feature: { - dashboard: ['read'], - dev_tools: ['all'], - }, - spaces: ['*'], - }, - { - base: ['all'], - spaces: ['marketing', 'sales'], - }, - ], - }) - .expect(400); - - const role = await es.security.getRole({ name: 'role_to_update' }); - expect(role).toEqual({ - role_to_update: { - cluster: ['monitor'], - indices: [ - { - names: ['beats-*'], - privileges: ['write'], - allow_restricted_indices: false, - }, - ], - applications: [ - { - application: 'kibana-.kibana', - privileges: ['read'], - resources: ['*'], - }, - { - application: 'apm', - privileges: ['apm-privilege'], - resources: ['*'], - }, - ], - metadata: { - bar: 'old-metadata', - }, - run_as: [], - transient_metadata: { - enabled: true, - }, - }, - }); - }); - }); - - describe('Delete Role', () => { - it('should delete an existing role', async () => { - await es.security.putRole({ - name: 'role_to_delete', - body: { - cluster: ['monitor'], - indices: [ - { - names: ['beats-*'], - privileges: ['write'], - }, - ], - applications: [ - { - application: 'kibana-.kibana', - privileges: ['read'], - resources: ['*'], - }, - { - application: 'apm', - privileges: ['apm-privilege'], - resources: ['*'], - }, - ], - metadata: { - bar: 'old-metadata', - }, - }, - }); - await supertestAdminWithApiKey.delete('/api/security/role/role_to_delete').expect(204); - - const deletedRole = await es.security.getRole( - { name: 'role_to_delete' }, - { ignore: [404] } - ); - expect(deletedRole).toEqual({}); - }); - }); - - describe('Access', () => { - describe('public', () => { - it('reset session page', async () => { - const { status } = await supertestAdminWithCookieCredentials.get( - '/internal/security/reset_session_page.js' - ); - expect(status).toBe(200); - }); - }); - describe('Disabled', () => { - it('get shared saved object permissions', async () => { - const { body, status } = await supertestAdminWithCookieCredentials.get( - '/internal/security/_share_saved_object_permissions' - ); - svlCommonApi.assertApiNotFound(body, status); - }); - }); - }); - }); - }); -} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/telemetry/telemetry_config.ts b/x-pack/test_serverless/api_integration/test_suites/common/telemetry/telemetry_config.ts index b6ca1666181f2..33726b6af0f64 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/telemetry/telemetry_config.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/telemetry/telemetry_config.ts @@ -5,7 +5,8 @@ * 2.0. */ -import { expect } from 'expect'; +import expect from '@kbn/expect'; +import { expect as externalExpect } from 'expect'; import { SupertestWithRoleScopeType } from '@kbn/test-suites-xpack/api_integration/deployment_agnostic/services'; import { FtrProviderContext } from '../../../ftr_provider_context'; @@ -13,6 +14,8 @@ export default function telemetryConfigTest({ getService }: FtrProviderContext) const roleScopedSupertest = getService('roleScopedSupertest'); let supertestAdminWithApiKey: SupertestWithRoleScopeType; let supertestAdminWithCookieCredentials: SupertestWithRoleScopeType; + const retry = getService('retry'); + const retryTimeout = 20 * 1000; describe('/api/telemetry/v2/config API Telemetry config', function () { before(async () => { @@ -42,7 +45,7 @@ export default function telemetryConfigTest({ getService }: FtrProviderContext) it('GET should get the default config', async () => { const { body } = await supertestAdminWithApiKey.get('/api/telemetry/v2/config').expect(200); - expect(body).toMatchObject(baseConfig); + externalExpect(body).toMatchObject(baseConfig); }); it('GET should get updated labels after dynamically updating them', async () => { @@ -71,7 +74,13 @@ export default function telemetryConfigTest({ getService }: FtrProviderContext) .send({ 'telemetry.labels.journeyName': null }) .expect(200, { ok: true }); - await supertestAdminWithApiKey.get('/api/telemetry/v2/config').expect(200, initialConfig); + await retry.tryForTime(retryTimeout, async function retryTelemetryConfigGetRequest() { + const { body } = await supertestAdminWithApiKey.get('/api/telemetry/v2/config').expect(200); + expect(body).to.eql( + initialConfig, + `Expected the response body to match the intitial config, but got: [${body}]` + ); + }); }); }); } diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/config.feature_flags.ts b/x-pack/test_serverless/api_integration/test_suites/observability/config.feature_flags.ts index 20724f4f9ae16..3668b6742c828 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/config.feature_flags.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/config.feature_flags.ts @@ -22,13 +22,12 @@ export default createTestConfig({ // add feature flags kbnServerArgs: [ '--xpack.infra.enabled=true', - '--xpack.security.roleManagementEnabled=true', // enables custom roles - `--xpack.spaces.maxSpaces=100`, // enables spaces UI capabilities + '--xpack.security.roleManagementEnabled=true', // needed to check composite feautures in /observability/platform_security/authorization.ts ], // load tests in the index file testFiles: [require.resolve('./index.feature_flags.ts')], // include settings from project controller // https://github.com/elastic/project-controller/blob/main/internal/project/observability/config/elasticsearch.yml - esServerArgs: ['xpack.ml.dfa.enabled=false', 'xpack.security.authc.native_roles.enabled=true'], + esServerArgs: ['xpack.ml.dfa.enabled=false'], }); diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/index.feature_flags.ts b/x-pack/test_serverless/api_integration/test_suites/observability/index.feature_flags.ts index 44ac3675266f7..3cd47636831f3 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/index.feature_flags.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/index.feature_flags.ts @@ -12,7 +12,5 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./custom_threshold_rule')); loadTestFile(require.resolve('./infra')); loadTestFile(require.resolve('./platform_security')); - loadTestFile(require.resolve('../common/platform_security/roles_routes_feature_flag.ts')); - loadTestFile(require.resolve('../common/management/multiple_spaces_enabled.ts')); }); } diff --git a/x-pack/test_serverless/api_integration/test_suites/search/config.feature_flags.ts b/x-pack/test_serverless/api_integration/test_suites/search/config.feature_flags.ts index 09de0f17e6b63..9dac5bfb595c9 100644 --- a/x-pack/test_serverless/api_integration/test_suites/search/config.feature_flags.ts +++ b/x-pack/test_serverless/api_integration/test_suites/search/config.feature_flags.ts @@ -19,8 +19,6 @@ export default createTestConfig({ suiteTags: { exclude: ['skipSvlSearch'] }, // add feature flags kbnServerArgs: [ - '--xpack.security.roleManagementEnabled=true', // enables custom roles - `--xpack.spaces.maxSpaces=100`, // enables spaces UI capabilities `--xpack.searchIndices.enabled=true`, // global empty state FF ], // load tests in the index file @@ -28,5 +26,5 @@ export default createTestConfig({ // include settings from project controller // https://github.com/elastic/project-controller/blob/main/internal/project/esproject/config/elasticsearch.yml - esServerArgs: ['xpack.security.authc.native_roles.enabled=true'], + esServerArgs: [], }); diff --git a/x-pack/test_serverless/api_integration/test_suites/search/index.feature_flags.ts b/x-pack/test_serverless/api_integration/test_suites/search/index.feature_flags.ts index acd435d9d29ae..bbfbc47fe9184 100644 --- a/x-pack/test_serverless/api_integration/test_suites/search/index.feature_flags.ts +++ b/x-pack/test_serverless/api_integration/test_suites/search/index.feature_flags.ts @@ -10,8 +10,5 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('Serverless search API - feature flags', function () { loadTestFile(require.resolve('./search_indices')); - loadTestFile(require.resolve('./platform_security')); - loadTestFile(require.resolve('../common/platform_security/roles_routes_feature_flag.ts')); - loadTestFile(require.resolve('../common/management/multiple_spaces_enabled.ts')); }); } diff --git a/x-pack/test_serverless/api_integration/test_suites/search/index.ts b/x-pack/test_serverless/api_integration/test_suites/search/index.ts index b568e75960951..42b8d0dd90435 100644 --- a/x-pack/test_serverless/api_integration/test_suites/search/index.ts +++ b/x-pack/test_serverless/api_integration/test_suites/search/index.ts @@ -14,5 +14,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./cases/find_cases')); loadTestFile(require.resolve('./cases/post_case')); loadTestFile(require.resolve('./serverless_search')); + loadTestFile(require.resolve('./platform_security')); }); } diff --git a/x-pack/test_serverless/api_integration/test_suites/security/config.feature_flags.ts b/x-pack/test_serverless/api_integration/test_suites/security/config.feature_flags.ts index eb6270bab7ce1..6f6404ad497cf 100644 --- a/x-pack/test_serverless/api_integration/test_suites/security/config.feature_flags.ts +++ b/x-pack/test_serverless/api_integration/test_suites/security/config.feature_flags.ts @@ -18,14 +18,11 @@ export default createTestConfig({ }, suiteTags: { exclude: ['skipSvlSec'] }, // add feature flags - kbnServerArgs: [ - '--xpack.security.roleManagementEnabled=true', // enables custom roles - `--xpack.spaces.maxSpaces=100`, // enables spaces UI capabilities - ], + kbnServerArgs: [], // load tests in the index file testFiles: [require.resolve('./index.feature_flags.ts')], // include settings from project controller // https://github.com/elastic/project-controller/blob/main/internal/project/security/config/elasticsearch.yml - esServerArgs: ['xpack.ml.nlp.enabled=true', 'xpack.security.authc.native_roles.enabled=true'], + esServerArgs: ['xpack.ml.nlp.enabled=true'], }); diff --git a/x-pack/test_serverless/api_integration/test_suites/security/index.feature_flags.ts b/x-pack/test_serverless/api_integration/test_suites/security/index.feature_flags.ts index 5c591f3213149..de4c823dbbb62 100644 --- a/x-pack/test_serverless/api_integration/test_suites/security/index.feature_flags.ts +++ b/x-pack/test_serverless/api_integration/test_suites/security/index.feature_flags.ts @@ -8,9 +8,5 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { - describe('Serverless security API - feature flags', function () { - loadTestFile(require.resolve('./platform_security')); - loadTestFile(require.resolve('../common/platform_security/roles_routes_feature_flag.ts')); - loadTestFile(require.resolve('../common/management/multiple_spaces_enabled.ts')); - }); + describe('Serverless security API - feature flags', function () {}); } diff --git a/x-pack/test_serverless/api_integration/test_suites/security/index.ts b/x-pack/test_serverless/api_integration/test_suites/security/index.ts index a7cb3cea71049..98dbf046bac94 100644 --- a/x-pack/test_serverless/api_integration/test_suites/security/index.ts +++ b/x-pack/test_serverless/api_integration/test_suites/security/index.ts @@ -13,5 +13,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./cases')); loadTestFile(require.resolve('./cloud_security_posture')); + loadTestFile(require.resolve('./platform_security')); }); } diff --git a/x-pack/test_serverless/functional/config.base.ts b/x-pack/test_serverless/functional/config.base.ts index 1a3cd2ffd6a5b..f904c53195dcc 100644 --- a/x-pack/test_serverless/functional/config.base.ts +++ b/x-pack/test_serverless/functional/config.base.ts @@ -41,10 +41,6 @@ export function createTestConfig(options: CreateTestConfigOptions) { `--xpack.trigger_actions_ui.enableExperimental=${JSON.stringify([ 'isUsingRuleCreateFlyout', ])}`, - // custom native roles are enabled only for search and security projects - ...(options.serverlessProject !== 'oblt' - ? ['--xpack.security.roleManagementEnabled=true'] - : []), ...(options.kbnServerArgs ?? []), ], }, diff --git a/x-pack/test_serverless/functional/page_objects/svl_search_elasticsearch_start_page.ts b/x-pack/test_serverless/functional/page_objects/svl_search_elasticsearch_start_page.ts index 0e3bc4b82a298..1f4294d4c8f41 100644 --- a/x-pack/test_serverless/functional/page_objects/svl_search_elasticsearch_start_page.ts +++ b/x-pack/test_serverless/functional/page_objects/svl_search_elasticsearch_start_page.ts @@ -85,8 +85,8 @@ export function SvlSearchElasticsearchStartPageProvider({ getService }: FtrProvi }, async expectO11yTrialLink() { await testSubjects.existOrFail('startO11yTrialBtn'); - expect(await testSubjects.getAttribute('startO11yTrialBtn', 'href')).equal( - 'https://fake-cloud.elastic.co/projects/create/observability/start' + expect(await testSubjects.getAttribute('startO11yTrialBtn', 'href')).match( + /^https?\:\/\/.*\/projects\/create\/observability\/start/ ); expect(await testSubjects.getAttribute('startO11yTrialBtn', 'target')).equal('_blank'); }, diff --git a/x-pack/test_serverless/functional/page_objects/svl_search_index_detail_page.ts b/x-pack/test_serverless/functional/page_objects/svl_search_index_detail_page.ts index 161be50cae410..1b355138173d6 100644 --- a/x-pack/test_serverless/functional/page_objects/svl_search_index_detail_page.ts +++ b/x-pack/test_serverless/functional/page_objects/svl_search_index_detail_page.ts @@ -27,8 +27,8 @@ export function SvlSearchIndexDetailPageProvider({ getService }: FtrProviderCont }, async expectConnectionDetails() { await testSubjects.existOrFail('connectionDetailsEndpoint', { timeout: 2000 }); - expect(await (await testSubjects.find('connectionDetailsEndpoint')).getVisibleText()).to.be( - 'https://fakeprojectid.es.fake-domain.cld.elstc.co:443' + expect(await (await testSubjects.find('connectionDetailsEndpoint')).getVisibleText()).match( + /^https?\:\/\/.*(\:\d+)?/ ); }, async expectQuickStats() { @@ -42,6 +42,15 @@ export function SvlSearchIndexDetailPageProvider({ getService }: FtrProviderCont await quickStatsDocumentElem.click(); expect(await quickStatsDocumentElem.getVisibleText()).to.contain('Index Size\n0b'); }, + + async expectQuickStatsToHaveDocumentCount(count: number) { + const quickStatsElem = await testSubjects.find('quickStats'); + const quickStatsDocumentElem = await quickStatsElem.findByTestSubject( + 'QuickStatsDocumentCount' + ); + expect(await quickStatsDocumentElem.getVisibleText()).to.contain(`Document count\n${count}`); + }, + async expectQuickStatsAIMappings() { await testSubjects.existOrFail('quickStats', { timeout: 2000 }); const quickStatsElem = await testSubjects.find('quickStats'); diff --git a/x-pack/test_serverless/functional/test_suites/common/discover/context_awareness/extensions/_get_cell_renderers.ts b/x-pack/test_serverless/functional/test_suites/common/discover/context_awareness/extensions/_get_cell_renderers.ts index 0cf8aeedd257a..b8503e0f8dcab 100644 --- a/x-pack/test_serverless/functional/test_suites/common/discover/context_awareness/extensions/_get_cell_renderers.ts +++ b/x-pack/test_serverless/functional/test_suites/common/discover/context_awareness/extensions/_get_cell_renderers.ts @@ -105,8 +105,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const firstCell = await dataGrid.getCellElementExcludingControlColumns(0, 0); const lastCell = await dataGrid.getCellElementExcludingControlColumns(2, 0); - const firstServiceNameCell = await firstCell.findByTestSubject('serviceNameCell-java'); - const lastServiceNameCell = await lastCell.findByTestSubject('serviceNameCell-unknown'); + const firstServiceNameCell = await firstCell.findByTestSubject( + 'dataTableCellActionsPopover_service.name' + ); + const lastServiceNameCell = await lastCell.findByTestSubject( + 'dataTableCellActionsPopover_service.name' + ); expect(await firstServiceNameCell.getVisibleText()).to.be('product'); expect(await lastServiceNameCell.getVisibleText()).to.be('accounting'); }); @@ -130,7 +134,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await retry.try(async () => { const firstCell = await dataGrid.getCellElementExcludingControlColumns(0, 0); expect(await firstCell.getVisibleText()).to.be('product'); - await testSubjects.missingOrFail('*serviceNameCell*'); + await testSubjects.missingOrFail('dataTableCellActionsPopover_service.name'); }); }); }); @@ -277,8 +281,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await retry.try(async () => { firstCell = await dataGrid.getCellElementExcludingControlColumns(0, 1); lastCell = await dataGrid.getCellElementExcludingControlColumns(2, 1); - const firstServiceNameCell = await firstCell.findByTestSubject('serviceNameCell-java'); - const lastServiceNameCell = await lastCell.findByTestSubject('serviceNameCell-unknown'); + const firstServiceNameCell = await firstCell.findByTestSubject( + 'dataTableCellActionsPopover_service.name' + ); + const lastServiceNameCell = await lastCell.findByTestSubject( + 'dataTableCellActionsPopover_service.name' + ); expect(await firstServiceNameCell.getVisibleText()).to.be('product'); expect(await lastServiceNameCell.getVisibleText()).to.be('accounting'); }); @@ -308,7 +316,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await firstCell.getVisibleText()).to.be('product'); expect(await lastCell.getVisibleText()).to.be('accounting'); - await testSubjects.missingOrFail('*serviceNameCell*'); + await testSubjects.missingOrFail('dataTableCellActionsPopover_service.name'); }); }); }); diff --git a/x-pack/test_serverless/functional/test_suites/common/platform_security/api_keys.ts b/x-pack/test_serverless/functional/test_suites/common/platform_security/api_keys.ts index 897299bb265ed..3a6cc43e31486 100644 --- a/x-pack/test_serverless/functional/test_suites/common/platform_security/api_keys.ts +++ b/x-pack/test_serverless/functional/test_suites/common/platform_security/api_keys.ts @@ -6,47 +6,31 @@ */ import expect from '@kbn/expect'; -import { Client } from '@elastic/elasticsearch'; -import { ToolingLog } from '@kbn/tooling-log'; import { FtrProviderContext } from '../../../ftr_provider_context'; -async function clearAllApiKeys(esClient: Client, logger: ToolingLog) { - const existingKeys = await esClient.security.queryApiKeys(); - if (existingKeys.count > 0) { - await Promise.all( - existingKeys.api_keys.map(async (key) => { - await esClient.security.invalidateApiKey({ ids: [key.id] }); - }) - ); - } else { - logger.debug('No API keys to delete.'); - } -} - export default ({ getPageObjects, getService }: FtrProviderContext) => { const pageObjects = getPageObjects(['common', 'svlCommonPage', 'apiKeys']); const browser = getService('browser'); - const es = getService('es'); - const log = getService('log'); describe('API keys', function () { - // TimeoutError: Waiting for element to be located By(css selector, [data-test-subj="apiKeysCreatePromptButton"]) Wait timed out after 10028ms - this.tags(['failsOnMKI']); before(async () => { await pageObjects.svlCommonPage.loginAsAdmin(); }); - after(async () => { - await clearAllApiKeys(es, log); - }); - it('should create and delete API keys correctly', async () => { await pageObjects.common.navigateToUrl('management', 'security/api_keys', { shouldUseHashForSubUrl: false, }); - const apiKeyName = 'Happy API Key'; - await pageObjects.apiKeys.clickOnPromptCreateApiKey(); + // name needs to be unique because we will confirm deletion by name + const apiKeyName = `API Key ${Date.now()}`; + + // If there are any existing API keys (e.g. will occur on projects created with QAF), + // the table will be displayed. Otherwise, the empty prompt is displayed. + const isPromptPage = await pageObjects.apiKeys.isPromptPage(); + if (isPromptPage) await pageObjects.apiKeys.clickOnPromptCreateApiKey(); + else await pageObjects.apiKeys.clickOnTableCreateApiKey(); + expect(await browser.getCurrentUrl()).to.contain('app/management/security/api_keys/create'); expect(await pageObjects.apiKeys.getFlyoutTitleText()).to.be('Create API key'); @@ -61,7 +45,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { expect(await pageObjects.apiKeys.isApiKeyModalExists()).to.be(false); expect(newApiKeyCreation).to.be(`Created API key '${apiKeyName}'`); - await pageObjects.apiKeys.deleteAllApiKeyOneByOne(); + await pageObjects.apiKeys.ensureApiKeyExists(apiKeyName); + await pageObjects.apiKeys.deleteApiKeyByName(apiKeyName); + expect(await pageObjects.apiKeys.doesApiKeyExist(apiKeyName)).to.be(false); }); }); }; diff --git a/x-pack/test_serverless/functional/test_suites/common/platform_security/index.ts b/x-pack/test_serverless/functional/test_suites/common/platform_security/index.ts index 062aef5b9acfb..5f96ea70aee73 100644 --- a/x-pack/test_serverless/functional/test_suites/common/platform_security/index.ts +++ b/x-pack/test_serverless/functional/test_suites/common/platform_security/index.ts @@ -13,6 +13,8 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./api_keys')); loadTestFile(require.resolve('./navigation/avatar_menu')); + loadTestFile(require.resolve('./navigation/management_nav_cards')); loadTestFile(require.resolve('./user_profiles/user_profiles')); + loadTestFile(require.resolve('./roles.ts')); }); } diff --git a/x-pack/test_serverless/functional/test_suites/common/platform_security/navigation/management_nav_cards.ts b/x-pack/test_serverless/functional/test_suites/common/platform_security/navigation/management_nav_cards.ts index c9aa658cdc44f..0f175f4c812a2 100644 --- a/x-pack/test_serverless/functional/test_suites/common/platform_security/navigation/management_nav_cards.ts +++ b/x-pack/test_serverless/functional/test_suites/common/platform_security/navigation/management_nav_cards.ts @@ -46,22 +46,28 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { expect(url).to.contain('/management/security/api_keys'); }); - it('displays the roles management card, and will navigate to the Roles UI', async () => { - await pageObjects.svlManagementPage.assertRoleManagementCardExists(); - await pageObjects.svlManagementPage.clickRoleManagementCard(); + describe('custom roles', function () { + this.tags('skipSvlOblt'); // Observability will not support custom roles - const url = await browser.getCurrentUrl(); - expect(url).to.contain('/management/security/roles'); + it('displays the roles management card, and will navigate to the Roles UI', async () => { + await pageObjects.svlManagementPage.assertRoleManagementCardExists(); + await pageObjects.svlManagementPage.clickRoleManagementCard(); + + const url = await browser.getCurrentUrl(); + expect(url).to.contain('/management/security/roles'); + }); }); - describe('Organization members', function () { - this.tags('skipSvlOblt'); // Observability will not support custom roles + describe('organization members', function () { + // Observability will not support custom roles + // Cannot test cloud link on MKI (will redirect to login) + this.tags(['skipSvlOblt', 'skipMKI']); + it('displays the Organization members management card, and will navigate to the cloud organization URL', async () => { await pageObjects.svlManagementPage.assertOrgMembersManagementCardExists(); await pageObjects.svlManagementPage.clickOrgMembersManagementCard(); const url = await browser.getCurrentUrl(); - // `--xpack.cloud.organization_url: '/account/members'`, expect(url).to.contain('/account/members'); }); }); @@ -101,7 +107,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); describe('Organization members', function () { - this.tags('skipSvlOblt'); // Observability will not support custom roles + // Observability will not support custom roles + // Cannot test cloud link on MKI (will redirect to login) + this.tags(['skipSvlOblt', 'skipMKI']); + it('displays the organization members management card, and will navigate to the cloud organization URL', async () => { // The org members nav card is always visible because there is no way to check if a user has approprite privileges await pageObjects.svlManagementPage.assertOrgMembersManagementCardExists(); diff --git a/x-pack/test_serverless/functional/test_suites/common/platform_security/roles.ts b/x-pack/test_serverless/functional/test_suites/common/platform_security/roles.ts index a57f82f158ebf..c402ad42f4fca 100644 --- a/x-pack/test_serverless/functional/test_suites/common/platform_security/roles.ts +++ b/x-pack/test_serverless/functional/test_suites/common/platform_security/roles.ts @@ -19,6 +19,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const platformSecurityUtils = getService('platformSecurityUtils'); describe('Roles', function () { + // custom roles are not enabled for observability projects + this.tags(['skipSvlOblt']); + describe('as Viewer', () => { before(async () => { await pageObjects.svlCommonPage.loginAsViewer(); diff --git a/x-pack/test_serverless/functional/test_suites/common/spaces/index.ts b/x-pack/test_serverless/functional/test_suites/common/spaces/index.ts index e9648b0339ac3..48dcebf486618 100644 --- a/x-pack/test_serverless/functional/test_suites/common/spaces/index.ts +++ b/x-pack/test_serverless/functional/test_suites/common/spaces/index.ts @@ -11,6 +11,7 @@ export default ({ loadTestFile }: FtrProviderContext) => { describe('Spaces', function () { this.tags(['esGate']); + loadTestFile(require.resolve('./spaces_management.ts')); loadTestFile(require.resolve('./spaces_selection.ts')); }); }; diff --git a/x-pack/test_serverless/functional/test_suites/common/spaces/multiple_spaces_enabled.ts b/x-pack/test_serverless/functional/test_suites/common/spaces/spaces_management.ts similarity index 58% rename from x-pack/test_serverless/functional/test_suites/common/spaces/multiple_spaces_enabled.ts rename to x-pack/test_serverless/functional/test_suites/common/spaces/spaces_management.ts index 84c7291e6e7ad..49ca03e5861e7 100644 --- a/x-pack/test_serverless/functional/test_suites/common/spaces/multiple_spaces_enabled.ts +++ b/x-pack/test_serverless/functional/test_suites/common/spaces/spaces_management.ts @@ -15,51 +15,9 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getPageObject, getService }: FtrProviderContext) { const svlCommon = getPageObject('common'); const svlCommonPage = getPageObject('svlCommonPage'); - const svlCommonNavigation = getService('svlCommonNavigation'); const testSubjects = getService('testSubjects'); describe('spaces', function () { - describe('selection', function () { - describe('as Viewer', function () { - before(async () => { - await svlCommonPage.loginAsViewer(); - }); - - it('displays the space selection menu in header', async () => { - await svlCommonNavigation.navigateToKibanaHome(); - await svlCommonPage.assertProjectHeaderExists(); - - await testSubjects.existOrFail('spacesNavSelector'); - }); - - it(`does not display the manage button in the space selection menu`, async () => { - await svlCommonNavigation.navigateToKibanaHome(); - await svlCommonPage.assertProjectHeaderExists(); - await testSubjects.click('spacesNavSelector'); - await testSubjects.missingOrFail('manageSpaces'); - }); - }); - - describe('as Admin', function () { - before(async () => { - await svlCommonPage.loginAsAdmin(); - }); - - it('displays the space selection menu in header', async () => { - await svlCommonNavigation.navigateToKibanaHome(); - await svlCommonPage.assertProjectHeaderExists(); - await testSubjects.existOrFail('spacesNavSelector'); - }); - - it(`displays the manage button in the space selection menu`, async () => { - await svlCommonNavigation.navigateToKibanaHome(); - await svlCommonPage.assertProjectHeaderExists(); - await testSubjects.click('spacesNavSelector'); - await testSubjects.existOrFail('manageSpaces'); - }); - }); - }); - describe('management', function () { describe('as Viewer', function () { before(async () => { diff --git a/x-pack/test_serverless/functional/test_suites/common/spaces/spaces_selection.ts b/x-pack/test_serverless/functional/test_suites/common/spaces/spaces_selection.ts index 526d0b3db5a41..a903d8778b7d8 100644 --- a/x-pack/test_serverless/functional/test_suites/common/spaces/spaces_selection.ts +++ b/x-pack/test_serverless/functional/test_suites/common/spaces/spaces_selection.ts @@ -12,18 +12,46 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { const svlCommonNavigation = getService('svlCommonNavigation'); const testSubjects = getService('testSubjects'); - // Skipped due to change in QA environment for role management and spaces - // TODO: revisit once the change is rolled out to all environments - describe.skip('space selection', function () { - before(async () => { - await svlCommonPage.loginAsViewer(); - }); + describe('spaces', function () { + describe('selection', function () { + describe('as Viewer', function () { + before(async () => { + await svlCommonPage.loginAsViewer(); + }); + + it('displays the space selection menu in header', async () => { + await svlCommonNavigation.navigateToKibanaHome(); + await svlCommonPage.assertProjectHeaderExists(); + + await testSubjects.existOrFail('spacesNavSelector'); + }); + + it(`does not display the manage button in the space selection menu`, async () => { + await svlCommonNavigation.navigateToKibanaHome(); + await svlCommonPage.assertProjectHeaderExists(); + await testSubjects.click('spacesNavSelector'); + await testSubjects.missingOrFail('manageSpaces'); + }); + }); + + describe('as Admin', function () { + before(async () => { + await svlCommonPage.loginAsAdmin(); + }); - it('does not have the space selection menu in header', async () => { - await svlCommonNavigation.navigateToKibanaHome(); - await svlCommonPage.assertProjectHeaderExists(); + it('displays the space selection menu in header', async () => { + await svlCommonNavigation.navigateToKibanaHome(); + await svlCommonPage.assertProjectHeaderExists(); + await testSubjects.existOrFail('spacesNavSelector'); + }); - await testSubjects.missingOrFail('spacesNavSelector'); + it(`displays the manage button in the space selection menu`, async () => { + await svlCommonNavigation.navigateToKibanaHome(); + await svlCommonPage.assertProjectHeaderExists(); + await testSubjects.click('spacesNavSelector'); + await testSubjects.existOrFail('manageSpaces'); + }); + }); }); }); } diff --git a/x-pack/test_serverless/functional/test_suites/observability/config.feature_flags.ts b/x-pack/test_serverless/functional/test_suites/observability/config.feature_flags.ts index 8d85455f4588a..ba07c22e6ab01 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/config.feature_flags.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/config.feature_flags.ts @@ -22,10 +22,6 @@ export default createTestConfig({ '--xpack.infra.enabled=true', '--xpack.infra.featureFlags.customThresholdAlertsEnabled=true', '--xpack.security.roleManagementEnabled=true', - `--xpack.cloud.serverless.project_id='fakeprojectid'`, - `--xpack.cloud.base_url='https://cloud.elastic.co'`, - `--xpack.cloud.organization_url='/account/members'`, - `--xpack.spaces.maxSpaces=100`, // enables spaces UI capabilities ], // load tests in the index file testFiles: [require.resolve('./index.feature_flags.ts')], diff --git a/x-pack/test_serverless/functional/test_suites/observability/index.feature_flags.ts b/x-pack/test_serverless/functional/test_suites/observability/index.feature_flags.ts index 1f087233b52e9..df3c826c20813 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/index.feature_flags.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/index.feature_flags.ts @@ -12,8 +12,5 @@ export default function ({ loadTestFile }: FtrProviderContext) { // add tests that require feature flags, defined in config.feature_flags.ts loadTestFile(require.resolve('./role_management')); loadTestFile(require.resolve('./infra')); - loadTestFile(require.resolve('../common/platform_security/navigation/management_nav_cards.ts')); - loadTestFile(require.resolve('../common/platform_security/roles.ts')); - loadTestFile(require.resolve('../common/spaces/multiple_spaces_enabled.ts')); }); } diff --git a/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/data_source_selector.ts b/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/data_source_selector.ts index f571fe4e0e462..ba12ebc153ca8 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/data_source_selector.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/data_source_selector.ts @@ -214,25 +214,25 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should sort the integrations list by the clicked sorting option', async () => { // Test ascending order - await PageObjects.observabilityLogsExplorer.clickSortButtonBy('asc'); - await retry.try(async () => { + await PageObjects.observabilityLogsExplorer.clickSortButtonBy('desc'); + await PageObjects.observabilityLogsExplorer.clickSortButtonBy('asc'); const { integrations } = await PageObjects.observabilityLogsExplorer.getIntegrations(); expect(integrations).to.eql(initialPackagesTexts); }); // Test descending order - await PageObjects.observabilityLogsExplorer.clickSortButtonBy('desc'); - await retry.try(async () => { + await PageObjects.observabilityLogsExplorer.clickSortButtonBy('asc'); + await PageObjects.observabilityLogsExplorer.clickSortButtonBy('desc'); const { integrations } = await PageObjects.observabilityLogsExplorer.getIntegrations(); expect(integrations).to.eql(initialPackagesTexts.slice().reverse()); }); // Test back ascending order - await PageObjects.observabilityLogsExplorer.clickSortButtonBy('asc'); - await retry.try(async () => { + await PageObjects.observabilityLogsExplorer.clickSortButtonBy('desc'); + await PageObjects.observabilityLogsExplorer.clickSortButtonBy('asc'); const { integrations } = await PageObjects.observabilityLogsExplorer.getIntegrations(); expect(integrations).to.eql(initialPackagesTexts); }); diff --git a/x-pack/test_serverless/functional/test_suites/search/config.feature_flags.ts b/x-pack/test_serverless/functional/test_suites/search/config.feature_flags.ts index 592da3d368c0d..b7c818821d36c 100644 --- a/x-pack/test_serverless/functional/test_suites/search/config.feature_flags.ts +++ b/x-pack/test_serverless/functional/test_suites/search/config.feature_flags.ts @@ -20,19 +20,13 @@ export default createTestConfig({ // add feature flags kbnServerArgs: [ `--xpack.cloud.id=ES3_FTR_TESTS:ZmFrZS1kb21haW4uY2xkLmVsc3RjLmNvJGZha2Vwcm9qZWN0aWQuZXMkZmFrZXByb2plY3RpZC5rYg==`, - `--xpack.cloud.serverless.project_id=fakeprojectid`, - `--xpack.cloud.base_url=https://fake-cloud.elastic.co`, - `--xpack.cloud.projects_url=/projects/`, - `--xpack.cloud.organization_url=/account/members`, - `--xpack.security.roleManagementEnabled=true`, - `--xpack.spaces.maxSpaces=100`, // enables spaces UI capabilities ], // load tests in the index file testFiles: [require.resolve('./index.feature_flags.ts')], // include settings from project controller // https://github.com/elastic/project-controller/blob/main/internal/project/esproject/config/elasticsearch.yml - esServerArgs: ['xpack.security.authc.native_roles.enabled=true'], + esServerArgs: [], apps: { serverlessElasticsearch: { pathname: '/app/elasticsearch/getting_started', diff --git a/x-pack/test_serverless/functional/test_suites/search/config.ts b/x-pack/test_serverless/functional/test_suites/search/config.ts index b01c80ec2dbb7..f330546373525 100644 --- a/x-pack/test_serverless/functional/test_suites/search/config.ts +++ b/x-pack/test_serverless/functional/test_suites/search/config.ts @@ -20,16 +20,8 @@ export default createTestConfig({ esServerArgs: [], kbnServerArgs: [ `--xpack.cloud.id=ES3_FTR_TESTS:ZmFrZS1kb21haW4uY2xkLmVsc3RjLmNvJGZha2Vwcm9qZWN0aWQuZXMkZmFrZXByb2plY3RpZC5rYg==`, - `--xpack.cloud.serverless.project_id=fakeprojectid`, `--xpack.cloud.serverless.project_name=ES3_FTR_TESTS`, - `--xpack.cloud.base_url=https://fake-cloud.elastic.co`, - `--xpack.cloud.profile_url=/user/settings/`, - `--xpack.cloud.billing_url=/billing/overview/`, - `--xpack.cloud.deployments_url=/deployments`, `--xpack.cloud.deployment_url=/projects/elasticsearch/fakeprojectid`, - `--xpack.cloud.users_and_roles_url=/account/members/`, - `--xpack.cloud.projects_url=/projects/`, - `--xpack.cloud.organization_url=/account/`, ], apps: { serverlessElasticsearch: { diff --git a/x-pack/test_serverless/functional/test_suites/search/elasticsearch_start.ts b/x-pack/test_serverless/functional/test_suites/search/elasticsearch_start.ts index 7ff1505304761..129f769283b34 100644 --- a/x-pack/test_serverless/functional/test_suites/search/elasticsearch_start.ts +++ b/x-pack/test_serverless/functional/test_suites/search/elasticsearch_start.ts @@ -27,9 +27,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }; describe('Elasticsearch Start [Onboarding Empty State]', function () { - // fails on MKI, see https://github.com/elastic/kibana/issues/196981 - this.tags(['failsOnMKI']); - describe('developer', function () { before(async () => { await pageObjects.svlCommonPage.loginWithRole('developer'); diff --git a/x-pack/test_serverless/functional/test_suites/search/getting_started.ts b/x-pack/test_serverless/functional/test_suites/search/getting_started.ts index dac9b2d7dae94..4a2c34d98eebc 100644 --- a/x-pack/test_serverless/functional/test_suites/search/getting_started.ts +++ b/x-pack/test_serverless/functional/test_suites/search/getting_started.ts @@ -15,7 +15,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { describe('getting started', function () { before(async () => { - await pageObjects.svlCommonPage.loginAsAdmin(); + await pageObjects.svlCommonPage.loginWithRole('developer'); }); it('has serverless side nav', async () => { diff --git a/x-pack/test_serverless/functional/test_suites/search/index.feature_flags.ts b/x-pack/test_serverless/functional/test_suites/search/index.feature_flags.ts index bc9e19f2ae71f..f33776926bd26 100644 --- a/x-pack/test_serverless/functional/test_suites/search/index.feature_flags.ts +++ b/x-pack/test_serverless/functional/test_suites/search/index.feature_flags.ts @@ -10,9 +10,5 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('serverless search UI - feature flags', function () { // add tests that require feature flags, defined in config.feature_flags.ts - - loadTestFile(require.resolve('../common/platform_security/navigation/management_nav_cards.ts')); - loadTestFile(require.resolve('../common/platform_security/roles.ts')); - loadTestFile(require.resolve('../common/spaces/multiple_spaces_enabled.ts')); }); } diff --git a/x-pack/test_serverless/functional/test_suites/search/navigation.ts b/x-pack/test_serverless/functional/test_suites/search/navigation.ts index f72bc70b1ee1d..a6da7b1467e9a 100644 --- a/x-pack/test_serverless/functional/test_suites/search/navigation.ts +++ b/x-pack/test_serverless/functional/test_suites/search/navigation.ts @@ -19,6 +19,8 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { const header = getPageObject('header'); describe('navigation', function () { + // see details: https://github.com/elastic/kibana/issues/196823 + this.tags(['failsOnMKI']); before(async () => { await svlCommonPage.loginWithRole('developer'); await svlSearchNavigation.navigateToLandingPage(); @@ -128,21 +130,6 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ deepLinkId: 'dashboards', }); - // check Other tools - // > Maps - await solutionNavigation.sidenav.clickLink({ - deepLinkId: 'maps', - }); - await solutionNavigation.sidenav.expectLinkActive({ - deepLinkId: 'maps', - }); - await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Other tools' }); - await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ - text: 'Maps', - }); - await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ - deepLinkId: 'maps', - }); // check Getting Started await solutionNavigation.sidenav.clickLink({ diff --git a/x-pack/test_serverless/functional/test_suites/search/search_index_detail.ts b/x-pack/test_serverless/functional/test_suites/search/search_index_detail.ts index 9e6a46e76d8f0..f6444bedc5bac 100644 --- a/x-pack/test_serverless/functional/test_suites/search/search_index_detail.ts +++ b/x-pack/test_serverless/functional/test_suites/search/search_index_detail.ts @@ -25,9 +25,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const indexName = 'test-my-index'; describe('Search index detail page', function () { - // fails on MKI, see https://github.com/elastic/kibana/issues/196981 - this.tags(['failsOnMKI']); - before(async () => { await pageObjects.svlCommonPage.loginWithRole('developer'); await pageObjects.svlApiKeys.deleteAPIKeys(); @@ -110,7 +107,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await pageObjects.embeddedConsole.clickEmbeddedConsoleControlBar(); }); - describe('With data', () => { + // FLAKY: https://github.com/elastic/kibana/issues/197144 + describe.skip('With data', () => { before(async () => { await es.index({ index: indexName, @@ -120,6 +118,9 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); await svlSearchNavigation.navigateToIndexDetailPage(indexName); }); + it('should have index documents', async () => { + await pageObjects.svlSearchIndexDetailPage.expectHasIndexDocuments(); + }); it('menu action item should be replaced with playground', async () => { await pageObjects.svlSearchIndexDetailPage.expectActionItemReplacedWhenHasDocs(); }); @@ -127,8 +128,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await pageObjects.svlSearchIndexDetailPage.clickMoreOptionsActionsButton(); await pageObjects.svlSearchIndexDetailPage.expectAPIReferenceDocLinkExistsInMoreOptions(); }); - it('should have index documents', async () => { - await pageObjects.svlSearchIndexDetailPage.expectHasIndexDocuments(); + it('should have one document in quick stats', async () => { + await pageObjects.svlSearchIndexDetailPage.expectQuickStatsToHaveDocumentCount(1); }); it('should have with data tabs', async () => { await pageObjects.svlSearchIndexDetailPage.expectWithDataTabsExists(); @@ -148,6 +149,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await pageObjects.svlSearchIndexDetailPage.withDataChangeTabs('dataTab'); await pageObjects.svlSearchIndexDetailPage.clickFirstDocumentDeleteAction(); await pageObjects.svlSearchIndexDetailPage.expectAddDocumentCodeExamples(); + await pageObjects.svlSearchIndexDetailPage.expectQuickStatsToHaveDocumentCount(0); }); }); diff --git a/x-pack/test_serverless/functional/test_suites/security/config.feature_flags.ts b/x-pack/test_serverless/functional/test_suites/security/config.feature_flags.ts index 84e80f154bee9..081887cae2380 100644 --- a/x-pack/test_serverless/functional/test_suites/security/config.feature_flags.ts +++ b/x-pack/test_serverless/functional/test_suites/security/config.feature_flags.ts @@ -18,16 +18,11 @@ export default createTestConfig({ }, suiteTags: { exclude: ['skipSvlSec'] }, // add feature flags - kbnServerArgs: [ - `--xpack.security.roleManagementEnabled=true`, - `--xpack.cloud.base_url='https://cloud.elastic.co'`, - `--xpack.cloud.organization_url='/account/members'`, - `--xpack.spaces.maxSpaces=100`, // enables spaces UI capabilities - ], + kbnServerArgs: [], // load tests in the index file testFiles: [require.resolve('./index.feature_flags.ts')], // include settings from project controller // https://github.com/elastic/project-controller/blob/main/internal/project/security/config/elasticsearch.yml - esServerArgs: ['xpack.ml.nlp.enabled=true', 'xpack.security.authc.native_roles.enabled=true'], + esServerArgs: ['xpack.ml.nlp.enabled=true'], }); diff --git a/x-pack/test_serverless/functional/test_suites/security/ftr/cloud_security_posture/agentless_api/create_agent.ts b/x-pack/test_serverless/functional/test_suites/security/ftr/cloud_security_posture/agentless_api/create_agent.ts index 8164fd39a81de..b26581fb46dfd 100644 --- a/x-pack/test_serverless/functional/test_suites/security/ftr/cloud_security_posture/agentless_api/create_agent.ts +++ b/x-pack/test_serverless/functional/test_suites/security/ftr/cloud_security_posture/agentless_api/create_agent.ts @@ -25,9 +25,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const AWS_SINGLE_ACCOUNT_TEST_ID = 'awsSingleTestId'; describe('Agentless API Serverless', function () { - // fails on MKI, see https://github.com/elastic/kibana/issues/196245 - this.tags(['failsOnMKI']); - let mockApiServer: http.Server; let cisIntegration: typeof pageObjects.cisAddIntegration; diff --git a/x-pack/test_serverless/functional/test_suites/security/index.feature_flags.ts b/x-pack/test_serverless/functional/test_suites/security/index.feature_flags.ts index 212632be442d3..45849d6063231 100644 --- a/x-pack/test_serverless/functional/test_suites/security/index.feature_flags.ts +++ b/x-pack/test_serverless/functional/test_suites/security/index.feature_flags.ts @@ -10,8 +10,5 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('serverless security UI - feature flags', function () { // add tests that require feature flags, defined in config.feature_flags.ts - loadTestFile(require.resolve('../common/platform_security/navigation/management_nav_cards.ts')); - loadTestFile(require.resolve('../common/platform_security/roles.ts')); - loadTestFile(require.resolve('../common/spaces/multiple_spaces_enabled.ts')); }); } diff --git a/x-pack/test_serverless/shared/config.base.ts b/x-pack/test_serverless/shared/config.base.ts index 1c702f02cff28..8b0be99b58ae4 100644 --- a/x-pack/test_serverless/shared/config.base.ts +++ b/x-pack/test_serverless/shared/config.base.ts @@ -151,7 +151,6 @@ export default async () => { // This ensures that we register the Security SAML API endpoints. // In the real world the SAML config is injected by control plane. `--plugin-path=${samlIdPPlugin}`, - '--xpack.cloud.id=ftr_fake_cloud_id', // Ensure that SAML is used as the default authentication method whenever a user navigates to Kibana. In other // words, Kibana should attempt to authenticate the user using the provider with the lowest order if the Login // Selector is disabled (which is how Serverless Kibana is configured). By declaring `cloud-basic` with a higher @@ -167,6 +166,16 @@ export default async () => { // configure security reponse header report-to settings to mimic MKI configuration `--csp.report_to=${JSON.stringify(['violations-endpoint'])}`, `--permissionsPolicy.report_to=${JSON.stringify(['violations-endpoint'])}`, + // normally below is injected by control plane + '--xpack.cloud.id=ftr_fake_cloud_id', + `--xpack.cloud.serverless.project_id=fakeprojectid`, + `--xpack.cloud.base_url=https://fake-cloud.elastic.co`, + `--xpack.cloud.projects_url=/projects/`, + `--xpack.cloud.profile_url=/user/settings/`, + `--xpack.cloud.billing_url=/billing/overview/`, + `--xpack.cloud.deployments_url=/deployments`, + `--xpack.cloud.organization_url=/account/`, + `--xpack.cloud.users_and_roles_url=/account/members/`, ], }, diff --git a/x-pack/test_serverless/tsconfig.json b/x-pack/test_serverless/tsconfig.json index d61c5c19a63db..92048160cb622 100644 --- a/x-pack/test_serverless/tsconfig.json +++ b/x-pack/test_serverless/tsconfig.json @@ -97,8 +97,8 @@ "@kbn/test-suites-src", "@kbn/console-plugin", "@kbn/cloud-security-posture-common", - "@kbn/security-plugin-types-common", "@kbn/core-saved-objects-import-export-server-internal", + "@kbn/security-plugin-types-common", "@kbn/ai-assistant-common", ] } diff --git a/yarn.lock b/yarn.lock index af4e2dfa05bcc..fc84740a0de45 100644 --- a/yarn.lock +++ b/yarn.lock @@ -435,7 +435,7 @@ js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.10.3", "@babel/parser@^7.12.11", "@babel/parser@^7.12.7", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.8", "@babel/parser@^7.23.0", "@babel/parser@^7.23.9", "@babel/parser@^7.24.7": +"@babel/parser@^7.1.0", "@babel/parser@^7.10.3", "@babel/parser@^7.12.11", "@babel/parser@^7.12.7", "@babel/parser@^7.14.7", "@babel/parser@^7.21.8", "@babel/parser@^7.23.0", "@babel/parser@^7.23.9", "@babel/parser@^7.24.7": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.7.tgz#9a5226f92f0c5c8ead550b750f5608e766c8ce85" integrity sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw== @@ -1366,7 +1366,7 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/types@7.21.2", "@babel/types@^7.0.0", "@babel/types@^7.10.3", "@babel/types@^7.12.11", "@babel/types@^7.12.7", "@babel/types@^7.20.7", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": +"@babel/types@7.21.2", "@babel/types@^7.0.0", "@babel/types@^7.10.3", "@babel/types@^7.12.11", "@babel/types@^7.12.7", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": version "7.21.2" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.2.tgz#92246f6e00f91755893c2876ad653db70c8310d1" integrity sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw== @@ -1460,11 +1460,6 @@ react-is "^18.2.0" react-shallow-renderer "^16.15.0" -"@cfworker/json-schema@^1.12.7": - version "1.12.7" - resolved "https://registry.yarnpkg.com/@cfworker/json-schema/-/json-schema-1.12.7.tgz#064d082a11881f684300bc7e6d3021e9d98f9a59" - integrity sha512-KEJUW22arGRQVoS6Ti8SvgXnme6NNMMcGBugdir1hf32ofWUXC8guwrFbepO2+YtqxNBUo5oO0pLYM5d4pyjOg== - "@cnakazawa/watch@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.3.tgz#099139eaec7ebf07a27c1786a3ff64f39464d2ef" @@ -2377,31 +2372,6 @@ resolved "https://registry.yarnpkg.com/@foliojs-fork/restructure/-/restructure-2.0.2.tgz#73759aba2aff1da87b7c4554e6839c70d43c92b4" integrity sha512-59SgoZ3EXbkfSX7b63tsou/SDGzwUEK6MuB5sKqgVK1/XE0fxmpsOb9DQI8LXW3KfGnAjImCGhhEb7uPPAUVNA== -"@formatjs/cli-lib@^6.3.8": - version "6.3.8" - resolved "https://registry.yarnpkg.com/@formatjs/cli-lib/-/cli-lib-6.3.8.tgz#10b685cf833c870d342f24de0f324acd7b02d31b" - integrity sha512-7+40WfAs9UE1BcjpUvtAkWk0REGchANpJZdHhXcMEVcKeDQKk0+OPhlueTnvFmYwENcJ+m7Leb26CRZDzGsc+Q== - dependencies: - "@formatjs/icu-messageformat-parser" "2.7.6" - "@formatjs/ts-transformer" "3.13.12" - "@types/estree" "^1.0.0" - "@types/fs-extra" "^9.0.1" - "@types/json-stable-stringify" "^1.0.32" - "@types/node" "14 || 16 || 17" - chalk "^4.0.0" - commander "8" - fast-glob "^3.2.7" - fs-extra "10" - json-stable-stringify "^1.0.1" - loud-rejection "^2.2.0" - tslib "^2.4.0" - typescript "5" - -"@formatjs/cli@^6.2.8": - version "6.2.8" - resolved "https://registry.yarnpkg.com/@formatjs/cli/-/cli-6.2.8.tgz#df471f79b30935d6c5e5956ad1e8321076ade767" - integrity sha512-sGtFehlHpNL5xbCkP5/zz5lgIVPPdQFqhY6Pcc9+whpBawln6VDzbaGA5ttWjtBDVVKZbEukiCFk8HW2H0OVxQ== - "@formatjs/ecma402-abstract@1.18.2": version "1.18.2" resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-1.18.2.tgz#bf103712a406874eb1e387858d5be2371ab3aa14" @@ -5671,6 +5641,10 @@ version "0.0.0" uid "" +"@kbn/manifest@link:packages/kbn-manifest": + version "0.0.0" + uid "" + "@kbn/mapbox-gl@link:packages/kbn-mapbox-gl": version "0.0.0" uid "" @@ -6195,6 +6169,10 @@ version "0.0.0" uid "" +"@kbn/response-ops-rule-params@link:packages/response-ops/rule_params": + version "0.0.0" + uid "" + "@kbn/response-stream-plugin@link:examples/response_stream": version "0.0.0" uid "" @@ -8487,12 +8465,12 @@ require-from-string "^2.0.2" uri-js-replace "^1.0.1" -"@redocly/cli@^1.25.5": - version "1.25.5" - resolved "https://registry.yarnpkg.com/@redocly/cli/-/cli-1.25.5.tgz#258f6d23e8298814518ec4d24d023c1e21e3b081" - integrity sha512-sFh4A8wqwuig7mF/nYNVIyxSfKKZikWC+uVH6OB1IepYQXNsHFaLAU1VaNI9gS5mMvWmYx5SEuSCVB9LaNFBhw== +"@redocly/cli@^1.25.6": + version "1.25.8" + resolved "https://registry.yarnpkg.com/@redocly/cli/-/cli-1.25.8.tgz#fecd62d9ee1d564e6f0e1522f2c5648f514ce02b" + integrity sha512-oVFN3rpGFqupx57ZS0mF2B8grnk3i0xjTQrrMm1oftF3GEf7yTg5JzwnWi8KKRWuxin4qI7j+Id5AKgNQNmTKA== dependencies: - "@redocly/openapi-core" "1.25.5" + "@redocly/openapi-core" "1.25.8" abort-controller "^3.0.0" chokidar "^3.5.1" colorette "^1.2.0" @@ -8506,7 +8484,7 @@ pluralize "^8.0.0" react "^17.0.0 || ^18.2.0" react-dom "^17.0.0 || ^18.2.0" - redoc "~2.1.5" + redoc "~2.2.0" semver "^7.5.2" simple-websocket "^9.0.0" styled-components "^6.0.7" @@ -8517,10 +8495,10 @@ resolved "https://registry.yarnpkg.com/@redocly/config/-/config-0.12.1.tgz#7b905a17d710244550ef826542d0db164d5ace02" integrity sha512-RW3rSirfsPdr0uvATijRDU3f55SuZV3m7/ppdTDvGw4IB0cmeZRkFmqTrchxMqWP50Gfg1tpHnjdxUCNo0E2qg== -"@redocly/openapi-core@1.25.5", "@redocly/openapi-core@^1.4.0": - version "1.25.5" - resolved "https://registry.yarnpkg.com/@redocly/openapi-core/-/openapi-core-1.25.5.tgz#443b1488c8ef1ddcb8f407c3e7dd8cb7b388b427" - integrity sha512-BNgXjqesJu4L5f8F73c2hkkH5IdvjYCKYFgIl+m9oNgqGRIPBJjtiEGOx7jkQ6nElN4311z7Z4aTECtklaaHwg== +"@redocly/openapi-core@1.25.8", "@redocly/openapi-core@^1.4.0": + version "1.25.8" + resolved "https://registry.yarnpkg.com/@redocly/openapi-core/-/openapi-core-1.25.8.tgz#a3aff052b1d9d2db8ba86263ec994bbc85f6b8f1" + integrity sha512-eKKRqo2RYo7UIoDvIgcUB9ynhOjIWJnILXFz+VDevYeOBKd/CxvC0KbNRnuOrFqG3ip6363R/ONal2MyvuVrjg== dependencies: "@redocly/ajv" "^8.11.2" "@redocly/config" "^0.12.1" @@ -10161,7 +10139,7 @@ dependencies: "@types/node" "*" -"@types/babel__core@*", "@types/babel__core@^7.1.14": +"@types/babel__core@^7.1.14": version "7.1.20" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.20.tgz#e168cdd612c92a2d335029ed62ac94c95b362359" integrity sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ== @@ -10172,17 +10150,6 @@ "@types/babel__template" "*" "@types/babel__traverse" "*" -"@types/babel__core@^7.20.5": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" - integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== - dependencies: - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" - "@types/babel__generator" "*" - "@types/babel__template" "*" - "@types/babel__traverse" "*" - "@types/babel__generator@*": version "7.0.2" resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.0.2.tgz#d2112a6b21fad600d7674274293c85dce0cb47fc" @@ -10190,20 +10157,6 @@ dependencies: "@babel/types" "^7.0.0" -"@types/babel__generator@^7.6.4": - version "7.6.4" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.4.tgz#1f20ce4c5b1990b37900b63f050182d28c2439b7" - integrity sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg== - dependencies: - "@babel/types" "^7.0.0" - -"@types/babel__helper-plugin-utils@^7.10.0": - version "7.10.0" - resolved "https://registry.yarnpkg.com/@types/babel__helper-plugin-utils/-/babel__helper-plugin-utils-7.10.0.tgz#dcd2416f9c189d5837ab2a276368cf67134efe78" - integrity sha512-60YtHzhQ9HAkToHVV+TB4VLzBn9lrfgrsOjiJMtbv/c1jPdekBxaByd6DMsGBzROXWoIL6U3lEFvvbu69RkUoA== - dependencies: - "@types/babel__core" "*" - "@types/babel__template@*": version "7.0.2" resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.0.2.tgz#4ff63d6b52eddac1de7b975a5223ed32ecea9307" @@ -10533,13 +10486,6 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== -"@types/event-stream@^4.0.5": - version "4.0.5" - resolved "https://registry.yarnpkg.com/@types/event-stream/-/event-stream-4.0.5.tgz#29f1be5f4c0de2e0312cf3b5f7146c975c08d918" - integrity sha512-pQ/RR/iuBW8K8WmwYaaC1nkZH0cHonNAIw6ktG8BCNrNuqNeERfBzNIAOq6Z7tvLzpjcMV02SZ5pxAekAYQpWA== - dependencies: - "@types/node" "*" - "@types/expect@^1.20.4": version "1.20.4" resolved "https://registry.yarnpkg.com/@types/expect/-/expect-1.20.4.tgz#8288e51737bf7e3ab5d7c77bfa695883745264e5" @@ -10585,11 +10531,6 @@ resolved "https://registry.yarnpkg.com/@types/file-saver/-/file-saver-2.0.0.tgz#cbb49815a5e1129d5f23836a98d65d93822409af" integrity sha512-dxdRrUov2HVTbSRFX+7xwUPlbGYVEZK6PrSqClg2QPos3PNe0bCajkDDkDeeC1znjSH03KOEqVbXpnJuWa2wgQ== -"@types/flat@^5.0.5": - version "5.0.5" - resolved "https://registry.yarnpkg.com/@types/flat/-/flat-5.0.5.tgz#2304df0b2b1e6dde50d81f029593e0a1bc2474d3" - integrity sha512-nPLljZQKSnac53KDUDzuzdRfGI0TDb5qPrb+SrQyN3MtdQrOnGsKniHN1iYZsJEBIVQve94Y6gNz22sgISZq+Q== - "@types/flot@^0.0.31": version "0.0.31" resolved "https://registry.yarnpkg.com/@types/flot/-/flot-0.0.31.tgz#0daca37c6c855b69a0a7e2e37dd0f84b3db8c8c1" @@ -10602,13 +10543,6 @@ resolved "https://registry.yarnpkg.com/@types/fnv-plus/-/fnv-plus-1.3.0.tgz#0f43f0b7e7b4b24de3a1cab69bfa009508f4c084" integrity sha512-ijls8MsO6Q9JUSd5w1v4y2ijM6S4D/nmOyI/FwcepvrZfym0wZhLdYGFD5TJID7tga0O3I7SmtK69RzpSJ1Fcw== -"@types/fs-extra@^9.0.1": - version "9.0.13" - resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.13.tgz#7594fbae04fe7f1918ce8b3d213f74ff44ac1f45" - integrity sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA== - dependencies: - "@types/node" "*" - "@types/geojson@*", "@types/geojson@^7946.0.10", "@types/geojson@^7946.0.7": version "7946.0.10" resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.10.tgz#6dfbf5ea17142f7f9a043809f1cd4c448cb68249" @@ -10914,13 +10848,6 @@ "@types/linkify-it" "*" "@types/mdurl" "*" -"@types/md5@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@types/md5/-/md5-2.2.0.tgz#cd82e16b95973f94bb03dee40c5b6be4a7fb7fb4" - integrity sha512-JN8OVL/wiDlCWTPzplsgMPu0uE9Q6blwp68rYsfk2G8aokRUQ8XD9MEhZwihfAiQvoyE+m31m6i3GFXwYWomKQ== - dependencies: - "@types/node" "*" - "@types/md5@^2.3.2": version "2.3.5" resolved "https://registry.yarnpkg.com/@types/md5/-/md5-2.3.5.tgz#481cef0a896e3a5dcbfc5a8a8b02c05958af48a5" @@ -11152,11 +11079,6 @@ "@types/node" "*" "@types/pdfkit" "*" -"@types/pegjs@^0.10.1": - version "0.10.1" - resolved "https://registry.yarnpkg.com/@types/pegjs/-/pegjs-0.10.1.tgz#9a2f3961dc62430fdb21061eb0ddbd890f9e3b94" - integrity sha512-ra8IchO9odGQmYKbm+94K58UyKCEKdZh9y0vxhG4pIpOJOBlC1C+ZtBVr6jLs+/oJ4pl+1p/4t3JtBA8J10Vvw== - "@types/picomatch@^2.3.0": version "2.3.0" resolved "https://registry.yarnpkg.com/@types/picomatch/-/picomatch-2.3.0.tgz#75db5e75a713c5a83d5b76780c3da84a82806003" @@ -11521,7 +11443,7 @@ "@types/methods" "^1.1.4" "@types/superagent" "^8.1.0" -"@types/tapable@^1", "@types/tapable@^1.0.5", "@types/tapable@^1.0.6": +"@types/tapable@^1", "@types/tapable@^1.0.5": version "1.0.6" resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.6.tgz#a9ca4b70a18b270ccb2bc0aaafefd1d486b7ea74" integrity sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA== @@ -11743,11 +11665,6 @@ dependencies: "@types/node" "*" -"@types/zen-observable@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.0.tgz#8b63ab7f1aa5321248aad5ac890a485656dcea4d" - integrity sha512-te5lMAWii1uEJ4FwLjzdlbw3+n0FZNOvFXHxQDKeT0dilh7HOzdMzV2TrJVUzq8ep7J4Na8OUYPRLSQkJHAlrg== - "@typescript-eslint/eslint-plugin@^5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" @@ -13402,11 +13319,6 @@ bare-path@^2.0.0, bare-path@^2.1.0: dependencies: bare-os "^2.1.0" -base64-arraybuffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc" - integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ== - base64-js@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" @@ -14768,11 +14680,6 @@ commander@7, commander@^7.0.0, commander@^7.2.0: resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== -commander@8: - version "8.3.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" - integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== - commander@^10.0.1: version "10.0.1" resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" @@ -14874,7 +14781,7 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -concat-stream@^1.4.7, concat-stream@^1.5.0, concat-stream@~1.6.0: +concat-stream@^1.5.0, concat-stream@~1.6.0: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== @@ -15274,13 +15181,6 @@ css-in-js-utils@^2.0.0: hyphenate-style-name "^1.0.2" isobject "^3.0.1" -css-line-break@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/css-line-break/-/css-line-break-2.1.0.tgz#bfef660dfa6f5397ea54116bb3cb4873edbc4fa0" - integrity sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w== - dependencies: - utrie "^1.0.2" - css-loader@^3.4.2, css-loader@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.6.0.tgz#2e4b2c7e6e2d27f8c8f28f61bffcd2e6c91ef645" @@ -15936,7 +15836,7 @@ dayjs@^1.10.4: resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0" integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ== -debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9: +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -18114,7 +18014,7 @@ fast-glob@^2.2.6: merge2 "^1.2.3" micromatch "^3.1.10" -fast-glob@^3.0.3, fast-glob@^3.2.11, fast-glob@^3.2.7, fast-glob@^3.2.9, fast-glob@^3.3.2: +fast-glob@^3.0.3, fast-glob@^3.2.11, fast-glob@^3.2.9, fast-glob@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -18461,7 +18361,7 @@ flat-cache@^3.0.4: flatted "^3.1.0" rimraf "^3.0.2" -flat@5, flat@^5.0.2: +flat@^5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== @@ -18506,11 +18406,6 @@ follow-redirects@^1.0.0, follow-redirects@^1.15.6: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== -font-awesome@4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133" - integrity sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM= - for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -18706,15 +18601,6 @@ fs-constants@^1.0.0: resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== -fs-extra@10, fs-extra@^10.0.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" - integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - fs-extra@^0.30.0: version "0.30.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" @@ -18726,6 +18612,15 @@ fs-extra@^0.30.0: path-is-absolute "^1.0.0" rimraf "^2.2.8" +fs-extra@^10.0.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-extra@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" @@ -19942,21 +19837,6 @@ html-webpack-plugin@^4.0.0: tapable "^1.1.3" util.promisify "1.0.0" -html2canvas@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/html2canvas/-/html2canvas-1.4.1.tgz#7cef1888311b5011d507794a066041b14669a543" - integrity sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA== - dependencies: - css-line-break "^2.1.0" - text-segmentation "^1.0.3" - -html@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/html/-/html-1.0.0.tgz#a544fa9ea5492bfb3a2cca8210a10be7b5af1f61" - integrity sha1-pUT6nqVJK/s6LMqCEKEL57WvH2E= - dependencies: - concat-stream "^1.4.7" - htmlparser2@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-4.1.0.tgz#9a4ef161f2e4625ebf7dfbe6c0a2f52d18a59e78" @@ -22812,14 +22692,6 @@ loud-rejection@^1.0.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" -loud-rejection@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-2.2.0.tgz#4255eb6e9c74045b0edc021fa7397ab655a8517c" - integrity sha512-S0FayMXku80toa5sZ6Ro4C+s+EtFDCsyJNG/AzFMfX3AxD5Si4dZsgzm/kKnbOxHl5Cv8jBlno8+3XYIh2pNjQ== - dependencies: - currently-unhandled "^0.4.1" - signal-exit "^3.0.2" - lower-case@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" @@ -24010,10 +23882,10 @@ msgpackr@^1.9.9: optionalDependencies: msgpackr-extract "^3.0.2" -msw@^2.4.9: - version "2.4.9" - resolved "https://registry.yarnpkg.com/msw/-/msw-2.4.9.tgz#350a84cedb90b578a32c7764431e3750900f8407" - integrity sha512-1m8xccT6ipN4PTqLinPwmzhxQREuxaEJYdx4nIbggxP8aM7r1e71vE7RtOUSQoAm1LydjGfZKy7370XD/tsuYg== +msw@^2.4.11: + version "2.4.11" + resolved "https://registry.yarnpkg.com/msw/-/msw-2.4.11.tgz#17001366c7c8de1540436d06d194f8facffed0f4" + integrity sha512-TVEw9NOPTc6ufOQLJ53234S9NBRxQbu7xFMxs+OCP43JQcNEIOKiZHxEm2nDzYIrwccoIhUxUf8wr99SukD76A== dependencies: "@bundled-es-modules/cookie" "^2.0.0" "@bundled-es-modules/statuses" "^1.0.1" @@ -24027,10 +23899,10 @@ msw@^2.4.9: graphql "^16.8.1" headers-polyfill "^4.0.2" is-node-process "^1.2.0" - outvariant "^1.4.2" + outvariant "^1.4.3" path-to-regexp "^6.3.0" strict-event-emitter "^0.5.1" - type-fest "^4.9.0" + type-fest "^4.26.1" yargs "^17.7.2" multicast-dns@^7.2.5: @@ -25021,7 +24893,7 @@ ospath@^1.2.2: resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" integrity sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs= -outvariant@^1.4.0, outvariant@^1.4.2, outvariant@^1.4.3: +outvariant@^1.4.0, outvariant@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/outvariant/-/outvariant-1.4.3.tgz#221c1bfc093e8fec7075497e7799fdbf43d14873" integrity sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA== @@ -27460,10 +27332,10 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" -redoc@~2.1.5: - version "2.1.5" - resolved "https://registry.yarnpkg.com/redoc/-/redoc-2.1.5.tgz#421307b22036b244171095bfc7ea3cfd419563c8" - integrity sha512-POSbVg+7WLf+/5/c6GWLxL7+9t2D+1WlZdLN0a6qaCQc+ih3XYzteRBkXEN5kjrYrRNjdspfxTZkDLN5WV3Tzg== +redoc@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/redoc/-/redoc-2.2.0.tgz#1043812696683f23e4b0fbe66fb50236fbe8b5bb" + integrity sha512-52rz/xJtpUBc3Y/GAkaX03czKhQXTxoU7WnkXNzRLuGwiGb/iEO4OgwcgQqtwHWrYNaZXTyqZ4MAVXpi/e1gAg== dependencies: "@cfaester/enzyme-adapter-react-18" "^0.8.0" "@redocly/openapi-core" "^1.4.0" @@ -30496,13 +30368,6 @@ text-hex@1.0.x: resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== -text-segmentation@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/text-segmentation/-/text-segmentation-1.0.3.tgz#52a388159efffe746b24a63ba311b6ac9f2d7943" - integrity sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw== - dependencies: - utrie "^1.0.2" - text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -31029,10 +30894,10 @@ type-fest@^1.2.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.4.0.tgz#e9fb813fe3bf1744ec359d55d1affefa76f14be1" integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA== -type-fest@^4.15.0, type-fest@^4.17.0, type-fest@^4.9.0: - version "4.18.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.18.3.tgz#5249f96e7c2c3f0f1561625f54050e343f1c8f68" - integrity sha512-Q08/0IrpvM+NMY9PA2rti9Jb+JejTddwmwmVQGskAlhtcrw1wsRzoR6ode6mR+OAabNa75w/dxedSUY2mlphaQ== +type-fest@^4.15.0, type-fest@^4.17.0, type-fest@^4.26.1: + version "4.26.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.26.1.tgz#a4a17fa314f976dd3e6d6675ef6c775c16d7955e" + integrity sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg== type-is@~1.6.18: version "1.6.18" @@ -31627,13 +31492,6 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= -utrie@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/utrie/-/utrie-1.0.2.tgz#d42fe44de9bc0119c25de7f564a6ed1b2c87a645" - integrity sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw== - dependencies: - base64-arraybuffer "^1.0.2" - uuid-browser@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/uuid-browser/-/uuid-browser-3.1.0.tgz#0f05a40aef74f9e5951e20efbf44b11871e56410"